悲观锁是将资源锁住,等一个之前获得锁的线程释放锁之后,下一个线程才可以访问。
乐观锁采取了一种宽泛的态度,通过某种方式不加锁来处理资源,可以使用版本号version和CAS算法实现,性能较悲观锁有很大的提高。乐观锁适用于多读的应用类型,这样可以提高吞吐量
CAS算法
CAS全拼又叫做compareAndSwap,从名字上的意思就知道是比较交换的意思
它包含 3 个参数 CAS(V,E,N),V表示要更新变量的值,E表示预期值,N表示新值。仅当 V值等于E值时,才会将V的值设为N,如果V值和E值不同,则说明已经有其他线程做两个更新,则当前线程则什么都不做。最后,CAS 返回当前V的真实值。
它涉及到三个操作数:内存值、预期值、新值
cas是一种基于锁的操作,而且是乐观锁。
CAS机制的优点
一开始在文中我们曾经提到过,cas是一种乐观锁,而且是一种非阻塞的轻量级的乐观锁,什么是非阻塞式的呢?其实就是一个线程想要获得锁,对方会给一个回应表示这个锁能不能获得。在资源竞争不激烈的情况下性能高,相比synchronized重量锁,synchronized会进行比较复杂的加锁,解锁和唤醒操作。
乐观锁的缺点:
1、ABA问题
解决办法:
JDK 1.5 以后的 AtomicStampedReference 类就提供了此种能力,其中的compareAndSet 方法就是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。
2、循环时间长导致cpu的开销大
自旋CAS如果一直没有获取锁或一直等待。
解决办法:
如果JVM支持处理器的PAUSE指令,两个作用:1、延长流水线执行指令(时间取决于实现的版本),使CPU不会消耗过多的资源;2、避免退出循环时因内存顺序冲突引起的CPU流水线被清空。
3、只能保证一个共享变量的原子操作
CAS 只对单个共享变量有效,当操作涉及跨多个共享变量时CAS 无效
解决办法:
JDK1.5后AtomicStampedReference类确保引用对象之间的原子性,可以把多个变量放在一个对象里来进行 CAS 操作.所以我们可以使用锁或者利用 AtomicReference 类把多个共享变量合并成一个共享变量来操作。
- 本文作者:
- 原文链接:
- 版权声明: 本博客所有文章除特别声明外,均采用 进行许可。转载请署名作者且注明文章出处。