JUC源码简析 CAS

相关阅读

简介

Compare and Swap,比较再交换;
CAS是一种无锁算法,有3个操作数:

  1. 内存值V
  2. 旧的预期值A
  3. 要修改的新值B;

当前仅当预期值A内存值V相同时,将内存值V修改为新值B,否则什么都不做。

do {
    从主内存备份旧数据;
    基于旧数据构造新数据;
} while (!CAS(内存地址, 备份的旧数据, 新数据));

开销

CAS是CPU指令级的操作,是一个原子操作,效率很高;
CAS避免了请求操作系统来裁定锁的问题,直接在CPU内部搞定;但有cache miss的情况。
举例:CPU0对一个变量执行CAS操作,但该变量是在CPU7的高速缓存中,则会进行如下简化的事件序列:

  1. CPU0检查本地高速缓存,没有缓存行;
  2. 请求被转发到CPU0和CPU1的互联模块,检查CPU1的本地高速缓存,没有找到缓存行;
  3. 请求被转发到系统互联模块,检查其他三个管芯,得知缓存行被CPU6和CPU7所在的管芯持有;
  4. 请求被转发到CPU6和CPU7的互联模块,检查这两个CPU的高速缓存,在CPU7的高速缓存中找到缓存行;
  5. CPU7将缓存行发送给所属的互联模块,并且刷新自己高速缓存中的缓存行;
  6. CPU6和CPU7的互联模块将缓存行发送给系统互联模块;
  7. 系统互联模块将缓存行发送给CPU0和CPU1的互联模块;
  8. CPU0和CPU1的互联模块见缓存行发送给CPU0的高速缓存;
  9. CPU0现在可以对高速缓存中的变量执行CAS操作;

JDK中应用——Unsafe

sun.misc.Unsafe中有很多使用CAS的方法,比如getAndAddInt,代码如下:

public final int getAndAddInt(Object o, long offset, int delta) {
    int v;
    do {
        v = getIntVolatile(o, offset);
    } while (!compareAndSwapInt(o, offset, v, v + delta));
    return v;
}

public final native boolean compareAndSwapInt(Object o, long offset, int expected, int x);

compareAndSwapInt底层代码如下:

int mp = os::is_MP();    //当前系统是否为多处理器
...
LOCK_IF_MP(mp)            //cmpxchg指令是否增加lock前缀
cmpxchg ...

intel手册对lock前缀的说明:

  1. 确保对内存的读-改-写操作原子执行;
  2. 禁止该指令与之前的和之后的读和写指令重排序;
  3. 把写缓冲区中的所有数据刷新到内存中;

第一点保证了CAS操作是一个原子操作,第二点和第三点所具有的内存屏障效果,保证CAS同时具有volatile读和volatile写的内存语义;

常见问题

  1. 只能保证一个变量的原子操作,多个变量如何保证?
    解决:将多个变量封装成对象,然后使用AtomicReference;

  2. 如何解决ABA问题?
    解决:使用AtomicStampedReference;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值