JUC并发编程——对CAS的理解和分析

CAS

CAS(CompareAndSet 或 CompareAndSwap)又称 无锁,乐观锁,实现方式是非阻塞同步。

CAS指令有三个操作数,分别是内存位置(在Java中可以简单地理解为变量的内存地址,用V表示)、旧的预期值(用A表示)和准备设置的新值(用B表示)。

CAS指令执行时,当且仅当V符合A时,处理器才会用B更新V的值,否则它就不执行更新。但是,不管是否更新了V的值,都会返回V的旧值,上述的处理过程是一个原子操作,执行期间不会被其他线程中断。CAS指令的底层是原子操作,保证了原子性,解决了指令交错问题。

例如 java.util.concurrent.atomic.AtomicInteger#compareAndSet 方法

/**
* @param expect the expected value
* @param update the new value
*	如果调用该方法的值是期望值(expect),那么更新 value = update,返回true
*/
public final boolean compareAndSet(int expect, int update) {
	return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

在JDK 5之后,Java类库中才开始使用CAS操作,该操作由sun.misc.Unsafe类里面的compareAndSwapInt()compareAndSwapLong()等几个方法包装提供。HotSpot虚拟机在内部对这些方法做了特殊处理,即时编译出来的结果就是一条平台相关的处理器CAS指令,没有方法调用的过程,或者可以认为是无条件内联进去了。

AtomicInteger等底层的 value 被 volatile 修饰,保证了可见性和禁止指令重排,可见性保证了多个线程间 CAS 操作,可以访问到最新的 value。

CAS和synchronized的对比

CAS 可以实现无锁并发,适用于线程数少、多核 CPU 的场景下。

  • CAS 是基于乐观锁的思想:其他线程可以修改共享变量,不加任何锁。
  • synchronized 是基于悲观锁的思想:给共享变量上锁,阻塞其他想获得锁的线程,直到锁的释放。

CAS 体现的是无锁并发、无阻塞并发

  • 因为没有使用 synchronized ,所以线程不会陷入阻塞,这是效率提升的因素之一
  • 但如果竞争激烈,可以想到重试必然频繁发生,反而效率会受影响。

ABA问题及解决

CAS操作存在一个逻辑漏洞:如果一个变量V初次读取的时候是A值,并且在准备赋值的时候检查到它仍然为A值,那就能说明它的值没有被其他线程改变过吗?这是不能的。因为如果在这段期间它的值曾经被改成B,后来又改为A,那CAS操作就会误认为它从来没有改变过。

change A -> B	true
change B -> A	true
change A -> C	true

解决方法:
控制变量值的版本来保证 CAS 的正确性。使用带有标记的原子引用类 AtomicStampedReference,为变量值提供版本号。
但是大部分情况下ABA问题不会影响程序并发的正确性,如果需要解决ABA问题,改用传统的互斥同步可能会比原子类更为高效。

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值