CAS原理分析

1 概述

CAS(Compare-and-Swap),即比较并替换,是一种实现并发算法时常用到的技术,Java并发包中的很多类都使用了CAS技术。CAS也是现在面试经常问的问题,本文将深入的介绍CAS的原理。

2 CAS原理

CAS通过调用JNI的代码实现的。JNI:Java Native Interface为JAVA本地调用,允许java调用其他语言。

而compareAndSwapInt就是借助C来调用CPU底层指令实现的。

下面是sun.misc.Unsafe类的compareAndSwapInt()方法的源代码,这就是传说中的CAS:

public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

可以看到这是个本地方法调用。这个本地方法在openjdk中依次调用的c++代码为:unsafe.cpp,atomic.cpp和atomicwindowsx86.inline.hpp。

下面是对应于intel x86处理器的源代码的片段:

// Adding a lock prefix to an instruction on MP machine
// VC++ doesn't like the lock prefix to be on a single line
// so we can't insert a label after the lock prefix.
// By emitting a lock prefix, we can define a label after it.
#define LOCK_IF_MP(mp) __asm cmp mp, 0  \
                       __asm je L0      \
                       __asm _emit 0xF0 \
                       __asm L0:

inline jint Atomic::cmpxchg (jint exchange_value, 
volatile jint* dest,  jint compare_value) {
  // alternative for InterlockedCompareExchange
  int mp = os::is_MP();
  __asm {
    mov edx, dest
    mov ecx, exchange_value
    mov eax, compare_value
    LOCK_IF_MP(mp)
    cmpxchg dword ptr [edx], ecx
  }
}

如上面源代码所示,因为cmpxchg 命令是不具备原子性的,程序会根据当前CPU的类型来决定是否为cmpxchg指令添加lock前缀。如果程序是在多核CPU上运行,就为cmpxchg指令加上lock前缀(lock cmpxchg)。相反,如果程序是在单核CPU上运行,就省略lock前缀(单核CPU自身会维护单处理器内的顺序一致性,不需要lock前缀提供的内存屏障效果)。

硬件:lock指令在执行后面指令的时候锁定一个北桥信号,但是当多CPU之间的竞争程度很高或者指令访问的内存地址未对齐时,可能会锁住总线。

3 优缺点

3.1优点

在并发量不是很高的情况下,由于使用CAS来添加锁机制是在操作系统的用户态内部实现,避免了向核心态申请锁,增加了效率。

3.2 缺点

CAS虽然很高效的解决了原子操作问题,但是CAS仍然存在三大问题。

  1. 循环时间长开销很大:CAS 通常是配合无限循环一起使用的,我们可以看到 getAndAddInt 方法执行时,如果 CAS 失败,会一直进行尝试。如果 CAS 长时间一直不成功,可能会给 CPU 带来很大的开销。
  2. 只能保证一个变量的原子操作:当对一个变量执行操作时,我们可以使用循环 CAS 的方式来保证原子操作,但是对多个变量操作时,CAS 目前无法直接保证操作的原子性。但是我们可以通过以下两种办法来解决:1)使用互斥锁来保证原子性;2)将多个变量封装成对象,通过 AtomicReference 来保证原子性。
  3. ABA问题:CAS 的使用流程通常如下:1)首先从地址 V 读取值 A;2)根据 A 计算目标值 B;3)通过 CAS 以原子的方式将地址 V 中的值从 A 修改为 B。但是在第1步中读取的值是A,并且在第3步修改成功了,我们就能说它的值在第1步和第3步之间没有被其他线程改变过了吗?如果在这段期间它的值曾经被改成了B,后来又被改回为A,那CAS操作就会误认为它从来没有被改变过。这个漏洞称为CAS操作的“ABA”问题。Java并发包为了解决这个问题,提供了一个带有标记的原子引用类“AtomicStampedReference”,它可以通过控制变量值的版本来保证CAS的正确性。因此,在使用CAS前要考虑清楚“ABA”问题是否会影响程序并发的正确性,如果需要解决ABA问题,改用传统的互斥同步可能会比原子类更高效。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值