悲观锁、乐观锁和自旋锁
文章目录
- 悲观锁
- 乐观锁
- 区别
- 自旋锁
- 参考
悲观锁
悲观锁 Pessimistic Lock:假定拿到的数据别人都会去修改,因此每次拿数据的时候都会上锁。
乐观锁
乐观锁 Optimistic Lock:假定拿到的数据别人都不会去修改,但在更新的时候会去判断这个数据有没有被修改过,可以使用版本号等机制,Java 使用 CAS。
乐观锁的缺点
- ABA 问题
CAS 会导致 “ABA 问题”。CAS 算法实现的一个重要前提是需要取出内存中某时刻的数据,而在下一时刻比较并替换,那么在这个时间差会导致数据的变化。
比如说一个线程 one 从内存位置 V 中取出 A,这时候另一个线程 two 也从内存中取出 A,并且 two 进行了一些操作变成了 B,然后 two 又将 V 位置的数据变成 A,这时候 one 进行 CAS 操作发现内存中任然是 A,然后 one 操作成功。尽管线程 one 的 CAS 操作成功,但是这并不代表这个过程就是没有问题的。
部分乐观锁的实现是通过版本号(version)的方式来解决 ABA 问题,乐观锁每次在执行数据的修改操作时,都会带上一个版本号,一旦版本号和数据的版本号一致就可以执行修改操作并对版本号执行 +1 操作,否则就执行失败,因为每次操作的版本号都会随着增加,所有不会出现 ABA 的问题。- 只能保证一个共享变量的原子操作
CAS 只对单个变量有效,但涉及到多个共享变量时 CAS 无效。
区别
两种锁各有优缺点
悲观锁适用于多写的情况,而乐观锁则不适用,因为在多写的情况下,乐观锁可能会有多次 retry 的情况 ,这样反倒降低性能。
自旋锁
自旋锁 Spin Lock 一个线程在获取锁的时候,如果锁被其他线程占用的话,它会一直等待直到获得锁。
自旋锁在获取不到锁的时候不会进入阻塞状态,从而进入阻塞态,也就是说它会一直处于用户态,这样也就减少了上下文切换的开销
参考
乐观锁和悲观锁的区别(最全面的分析)