乐观锁和悲观锁

本文最后更新于 2025年5月20日 14:36

乐观锁

乐观锁认为共享资源每次被访问的时候不会出现问题,线程可以不停地执行,无需加锁也无需等待,只是在提交修改的时候去验证对应的资源是否被其它线程修改了。乐观锁可以通过版本号机制和 CAS 算法实现。

CAS

  • V:要更新的变量值(Var)
  • E:预期值(Expected)
  • N:拟写入的新值(New)

当且仅当 V 的值等于 E 时,CAS 通过原子方式用新值 N 来更新 V 的值。如果不等,说明已经有其它线程更新了 V,则当前线程放弃更新。

JAVA 中如何实现 CAS

JAVA 中的 CAS 是通过底层的 Unsafe 类调用 CPU 的原子指令实现的。

  • Java 使用 Unsafe.compareAndSwapXXX 方法实现 CAS;(xxx = int / long / Object
  • 这个方法由 JNI 调用到底层 C++代码;(通过 JNI 可以把 Java 调用“桥接”到 C/C++ 实现的底层函数上)
  • 最终由 CPU 的原子指令(如 x86 的 CMPXCHG) 保证原子性。

CAS 存在的问题

  • ABA:对一个值的两次读取之间值可能被修改过。解决思路是在变量前面追加上版本号或者时间戳

悲观锁

悲观锁认为共享资源每次被访问的时候都会出现问题,所以每次获取资源时都会上锁,这样其他线程如果想拿到这个资源就会阻塞,直到锁被上一个持有者释放。

 Java 中synchronizedReentrantLock 等独占锁就是悲观锁思想的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
public void performSynchronisedTask() {
synchronized (this) {
// 需要同步的操作
}
}

private Lock lock = new ReentrantLock();
lock.lock();
try {
// 需要同步的操作
} finally {
lock.unlock();
}
  • 优点:实现简单,数据安全性强,适合写操作较多的场景,避免大量重试消耗资源。
  • 缺点:高并发场景下激烈锁竞争会造成大量线程阻塞,大量线程阻塞会导致大量上下文切换,消耗大量系统资源。悲观锁甚至会导致死锁问题。

乐观锁和悲观锁
http://example.com/2025/04/15/乐观锁和悲观锁/
作者
Moonike
发布于
2025年4月15日
更新于
2025年5月20日
许可协议