java中乐观锁,悲观锁,公平锁,非公平锁,死锁,可重入锁,同步锁,读写锁,表锁,行锁,间隙锁,排它锁,自旋锁 - 面试宝典

举报
皮牙子抓饭 发表于 2023/08/13 17:10:54 2023/08/13
【摘要】 Java中的锁机制包括乐观锁、悲观锁、公平锁、非公平锁、死锁、可重入锁、同步锁、读写锁、表锁、行锁、间隙锁、排它锁和自旋锁。下面逐一介绍:乐观锁:假设多个线程之间不会发生冲突,每个线程在进行操作时都不会加锁,只有在提交操作时才会对数据进行冲突检测。悲观锁:假设多个线程之间会发生冲突,每个线程在进行操作时先加锁,确保同一时间只有一个线程能访问共享资源。公平锁:按照线程请求锁的顺序来分配锁,保证...

Java中的锁机制包括乐观锁、悲观锁、公平锁、非公平锁、死锁、可重入锁、同步锁、读写锁、表锁、行锁、间隙锁、排它锁和自旋锁。下面逐一介绍:

  1. 乐观锁:假设多个线程之间不会发生冲突,每个线程在进行操作时都不会加锁,只有在提交操作时才会对数据进行冲突检测。
  2. 悲观锁:假设多个线程之间会发生冲突,每个线程在进行操作时先加锁,确保同一时间只有一个线程能访问共享资源。
  3. 公平锁:按照线程请求锁的顺序来分配锁,保证所有线程公平竞争锁的机会。
  4. 非公平锁:不按照线程请求锁的顺序来分配锁,允许线程插队抢占锁。
  5. 死锁:两个或多个线程互相持有对方所需的资源,导致所有线程都无法继续执行的情况。
  6. 可重入锁:也称为递归锁,同一个线程可以多次获得同一个锁,避免死锁。
  7. 同步锁:通过synchronized关键字实现的锁,保证同一时间只有一个线程能访问共享资源。
  8. 读写锁:也称为共享锁和排他锁,允许多个线程同时读取共享资源,但只有一个线程能写入共享资源。
  9. 表锁:对整个表进行锁定,防止其他事务对表进行修改操作。
  10. 行锁:对表中的行进行锁定,只锁定需要修改的行,提高并发性能。
  11. 间隙锁:锁定索引范围而不是实际的数据行,用于防止其他事务插入索引范围内的数据。
  12. 排它锁:也称为独占锁,只允许一个线程独占资源,其他线程无法访问。
  13. 自旋锁:在获取锁时,线程不会立即阻塞,而是循环检查锁的状态,直到获取到锁为止。 同步锁和异步锁是指在多线程环境下对共享资源进行操作的方式。同步锁是指在一个线程执行共享资源的操作时,其他线程需要等待,直到该线程释放锁;异步锁是指在一个线程执行共享资源的操作时,其他线程不需要等待,可以继续执行自己的操作。 排它锁是一种只允许一个线程独占资源的锁,其他线程无法访问。自旋锁是一种在获取锁时循环检查锁的状态的锁,避免线程阻塞等待锁的释放。

下面是Java中几种锁的代码示例:

  1. 同步锁(使用synchronized关键字):
javaCopy codepublic class SynchronizedExample {
    private int count = 0;
    public synchronized void increment() {
        count++;
    }
}
  1. 可重入锁(使用ReentrantLock类):
javaCopy codeimport java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
    private int count = 0;
    private ReentrantLock lock = new ReentrantLock();
    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }
}
  1. 读写锁(使用ReentrantReadWriteLock类):
javaCopy codeimport java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockExample {
    private int count = 0;
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    public void read() {
        lock.readLock().lock();
        try {
            // 读取共享资源
        } finally {
            lock.readLock().unlock();
        }
    }
    public void write() {
        lock.writeLock().lock();
        try {
            // 写入共享资源
        } finally {
            lock.writeLock().unlock();
        }
    }
}
  1. 自旋锁(使用AtomicInteger类的compareAndSet方法):
javaCopy codeimport java.util.concurrent.atomic.AtomicInteger;
public class SpinLockExample {
    private AtomicInteger lock = new AtomicInteger(0);
    public void lock() {
        while (!lock.compareAndSet(0, 1)) {
            // 自旋等待锁的释放
        }
    }
    public void unlock() {
        lock.set(0);
    }
}

以上是一些锁的基本代码示例,具体使用时需要根据实际场景进行适当的调整和扩展。

  1. 公平锁和非公平锁(使用ReentrantLock类):
javaCopy codeimport java.util.concurrent.locks.ReentrantLock;
public class FairNonFairLockExample {
    private int count = 0;
    // 公平锁
    private ReentrantLock fairLock = new ReentrantLock(true);
    // 非公平锁
    private ReentrantLock nonFairLock = new ReentrantLock(false);
    public void incrementWithFairLock() {
        fairLock.lock();
        try {
            count++;
        } finally {
            fairLock.unlock();
        }
    }
    public void incrementWithNonFairLock() {
        nonFairLock.lock();
        try {
            count++;
        } finally {
            nonFairLock.unlock();
        }
    }
}
  1. 死锁(简化示例):
javaCopy codepublic class DeadlockExample {
    private static final Object lock1 = new Object();
    private static final Object lock2 = new Object();
    public void method1() {
        synchronized (lock1) {
            synchronized (lock2) {
                // 执行操作
            }
        }
    }
    public void method2() {
        synchronized (lock2) {
            synchronized (lock1) {
                // 执行操作
            }
        }
    }
}

请注意,死锁是一种严重的问题,上述示例只是简化了死锁的情况,请在实际开发中避免出现死锁的情况。 这些代码示例仅作为理解锁概念和使用的参考,具体使用时需要根据实际情况进行适当的调整和扩展。

  1. 乐观锁和悲观锁(使用版本号或时间戳实现):
javaCopy codeimport java.util.concurrent.atomic.AtomicStampedReference;
public class OptimisticPessimisticLockExample {
    private AtomicStampedReference<Integer> value = new AtomicStampedReference<>(0, 0);
    // 乐观锁
    public void optimisticUpdate() {
        int currentStamp = value.getStamp();
        int currentValue = value.getReference();
        // 在更新前检查版本号
        if (value.compareAndSet(currentValue, currentValue + 1, currentStamp, currentStamp + 1)) {
            // 更新成功
        } else {
            // 更新失败,进行相应处理
        }
    }
    // 悲观锁
    public void pessimisticUpdate() {
        synchronized (this) {
            int currentValue = value.getReference();
            // 进行更新操作
            value.set(currentValue + 1, value.getStamp() + 1);
        }
    }
}
  1. 分段锁(使用ConcurrentHashMap类):
javaCopy codeimport java.util.concurrent.ConcurrentHashMap;
public class StripedLockExample {
    private ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
    private int segments = 16;
    private Object[] locks = new Object[segments];
    public StripedLockExample() {
        for (int i = 0; i < segments; i++) {
            locks[i] = new Object();
        }
    }
    public void update(String key) {
        int segment = Math.abs(key.hashCode() % segments);
        synchronized (locks[segment]) {
            // 进行更新操作
            map.put(key, map.getOrDefault(key, 0) + 1);
        }
    }
}

乐观锁和悲观锁是在并发编程中常用的两种思路,分段锁是一种在高并发场景下提高性能的技术。这些代码示例仅作为理解锁概念和使用的参考,具体使用时需要根据实际情况进行适当的调整和扩展。

  1. 自旋锁的优化(使用自适应自旋和锁消除):
javaCopy codeimport java.util.concurrent.atomic.AtomicInteger;
public class SpinLockOptimizationExample {
    private AtomicInteger lock = new AtomicInteger(0);
    public void lock() {
        int spinCount = 0;
        while (true) {
            if (lock.compareAndSet(0, 1)) {
                return;
            } else {
                if (spinCount < 10) {
                    spinCount++;
                } else {
                    Thread.yield(); // 自适应自旋
                }
            }
        }
    }
    public void unlock() {
        lock.set(0);
    }
}
  1. 读写锁的升降级(使用StampedLock类):
javaCopy codeimport java.util.concurrent.locks.StampedLock;
public class ReadWriteLockUpgradeExample {
    private int value = 0;
    private StampedLock lock = new StampedLock();
    public void read() {
        long stamp = lock.tryOptimisticRead();
        int currentValue = value;
        if (!lock.validate(stamp)) {
            stamp = lock.readLock();
            try {
                currentValue = value;
            } finally {
                lock.unlockRead(stamp);
            }
        }
        // 读取共享资源
    }
    public void write() {
        long stamp = lock.writeLock();
        try {
            // 写入共享资源
        } finally {
            lock.unlockWrite(stamp);
        }
    }
}

以上是一些锁的优化和高级用法的代码示例,具体使用时需要根据实际场景进行适当的调整和扩展。

  1. 条件锁(使用Condition类):
javaCopy codeimport java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionLockExample {
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    private boolean flag = false;
    public void waitMethod() throws InterruptedException {
        lock.lock();
        try {
            while (!flag) {
                condition.await();
            }
            // 执行操作
        } finally {
            lock.unlock();
        }
    }
    public void signalMethod() {
        lock.lock();
        try {
            flag = true;
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }
}
  1. 闭锁(使用CountDownLatch类):
javaCopy codeimport java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
    private static final int THREAD_COUNT = 3;
    private CountDownLatch latch = new CountDownLatch(THREAD_COUNT);
    public void doWork() throws InterruptedException {
        // 创建并启动工作线程
        for (int i = 0; i < THREAD_COUNT; i++) {
            Thread thread = new Thread(() -> {
                // 执行工作任务
                latch.countDown();
            });
            thread.start();
        }
        // 等待所有工作线程完成
        latch.await();
        // 所有工作线程完成后继续执行其他操作
    }
}

以上是一些常见的锁的使用场景,具体使用时需要根据实际需求进行适当调整和扩展。同时也要注意锁的使用要避免死锁和竞态条件等并发问题。

  1. 读写锁的降级(使用ReentrantReadWriteLock类):
javaCopy codeimport java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockDowngradeExample {
    private int value = 0;
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    public void readAndWrite() {
        lock.writeLock().lock();
        try {
            // 写入共享资源
            value++;
            // 降级为读锁
            lock.readLock().lock();
        } finally {
            lock.writeLock().unlock();
        }
        try {
            // 读取共享资源
            System.out.println(value);
        } finally {
            lock.readLock().unlock();
        }
    }
}
  1. 信号量(使用Semaphore类):
javaCopy codeimport java.util.concurrent.Semaphore;
public class SemaphoreExample {
    private Semaphore semaphore = new Semaphore(3);
    public void doWork() throws InterruptedException {
        // 获取信号量许可
        semaphore.acquire();
        try {
            // 执行任务
        } finally {
            // 释放信号量许可
            semaphore.release();
        }
    }
}

以上是一些进阶的锁及并发工具的使用示例,具体使用时需要根据实际情况进行适当的调整和扩展。同时,要注意合理使用锁和并发工具,避免产生死锁、竞态条件等问题。

  1. 阻塞队列(使用BlockingQueue接口):
javaCopy codeimport java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class BlockingQueueExample {
    private BlockingQueue<String> queue = new LinkedBlockingQueue<>();
    public void produce(String item) throws InterruptedException {
        queue.put(item);
    }
    public String consume() throws InterruptedException {
        return queue.take();
    }
}
  1. 倒计时门闩(使用CountDownLatch类):
javaCopy codeimport java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
    private CountDownLatch latch = new CountDownLatch(3);
    public void doWork() throws InterruptedException {
        // 创建并启动工作线程
        for (int i = 0; i < 3; i++) {
            Thread thread = new Thread(() -> {
                // 执行工作任务
                latch.countDown();
            });
            thread.start();
        }
        // 等待所有工作线程完成
        latch.await();
        // 所有工作线程完成后继续执行其他操作
    }
}

以上是一些常见的并发工具的使用示例,具体使用时需要根据实际需求进行适当调整和扩展。同时也要注意并发工具的正确使用,避免出现线程安全问题。

  1. 循环屏障(使用CyclicBarrier类):
javaCopy codeimport java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierExample {
    private static final int THREAD_COUNT = 3;
    private CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT, () -> {
        // 所有线程都到达屏障后执行的操作
        System.out.println("All threads have reached the barrier");
    });
    public void doWork() {
        // 创建并启动工作线程
        for (int i = 0; i < THREAD_COUNT; i++) {
            Thread thread = new Thread(() -> {
                try {
                    // 执行工作任务
                    System.out.println("Thread has reached the barrier");
                    barrier.await();
                    // 所有线程都到达屏障后继续执行其他任务
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            });
            thread.start();
        }
    }
}
  1. 定时任务(使用ScheduledExecutorService接口):
javaCopy codeimport java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScheduledTaskExample {
    private ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
    public void scheduleTask() {
        executor.schedule(() -> {
            // 执行定时任务
        }, 1, TimeUnit.SECONDS);
    }
}

以上是一些高级的并发工具的使用示例,具体使用时需要根据实际情况进行适当的调整和扩展。同时,要注意合理使用并发工具,避免产生死锁、竞态条件等问题,并保证线程安全性。

  1. 并发集合(使用ConcurrentHashMap类):
javaCopy codeimport java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class ConcurrentHashMapExample {
    private ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>();
    public void put(String key, int value) {
        map.put(key, value);
    }
    public int get(String key) {
        return map.get(key);
    }
}
  1. 并发队列(使用ConcurrentLinkedQueue类):
javaCopy codeimport java.util.concurrent.ConcurrentLinkedQueue;
public class ConcurrentQueueExample {
    private ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();
    public void enqueue(String item) {
        queue.add(item);
    }
    public String dequeue() {
        return queue.poll();
    }
}

以上是一些常见的并发集合和队列的使用示例,具体使用时需要根据实际需求进行适当调整和扩展。同时也要注意并发集合和队列的线程安全性,避免出现并发访问的问题。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。