乐观锁和悲观锁
乐观锁和悲观锁
- 悲观锁: 总是假设最坏的情况,即认为共享资源每次被访问时都会出现问题
- 乐观锁: 则是假设最好的情况,即认为共享资源每次被访问时不会出现问题
1. 悲观锁
- 悲观锁总是假设最坏的情况,认为共享资源每次被访问时都会出现问题(如共享数据被修改),所以每次获取资源操作时都会上锁。也就是:共享资源每次只给一个线程使用,其他线程阻塞,用完后再把资源转让给其他线程
- Java中的
synchronized关键字和ReentrantLock类都是悲观锁的实现方式:1
2
3
4
5
6
7
8
9
10
11
12
13public void performSynchronisedTask() {
synchronized(this) {
// 需要同步的操作
}
}
private Lock lock = new ReentrantLock();
lock.lock();
try {
// 需要同步的操作
} finally {
lock.unlock();
} - 高并发场景下,悲观锁会导致大量线程阻塞,影响系统性能,还会存在死锁问题
2. 乐观锁
- 乐观锁则是假设最好的情况,认为共享资源每次被访问时不会出现问题,所以线程可以不停地执行,无需枷锁也无需等待,只是在提交修改时去验证对应的资源是否被其他线程修改了(具体方法可以使用版本号机制或CAS算法)
- Java中
java.util.concurrent.atomic包下的原子类(如AtomicInteger、AtomicReference等)就是乐观锁的实现方式,这些类通过CAS(Compare-And-Swap)操作来实现线程安全:1
2
3
4// LongAdder 在高并发场景下会比 AtomicInteger 和 AtomicLong 的性能更好
// 代价就是会消耗更多的内存空间(空间换时间)
LongAdder sum = new LongAdder();
sum.increment(); - 高并发情况下,乐观锁不存在锁竞争造成的线程阻塞,也不会有死锁问题,性能上更好一些,但如果冲突频繁,可能会导致大量的重试操作,影响性能
- 理论上来说,悲观锁多用于写比较多的情况,乐观锁适用于写比较少的情况
