Java中CAS原理

引例:在执行i++;操作时由于不是原子操作,所以我们需将该复合操作进行加锁来保证该操作的原子性。一种方法是采用悲观锁策略synchronized关键字,另一种更高效的方法是采用硬件支撑的CAS(Compare-and-Swap)操作来实现。
CAS操作其实对应了硬件处理器底层的一个一个3操作数指令。在Java中该操作由sun.misc.Unsafe类里面的compareAndSwap***()提供支持,这一类方法是native方法,并且虚拟机对这类方法做了特殊处理,所以在编译期间将会编译成一条平台相关的CAS指令。

一条CAS的指令如下图所示(这里的操作数名字是我自己取得):

  1. 第一个操作数loc — 表示的是变量在主存中的地址(注意不是变量值本身,只有通过地址才能实时的观察到主存中变量的值,有的博文没有说明这一点,直接说成了该处存放的就是真实值)。
  2. 第二个操作数oldVal — 他表示我们上一次看到的主存中的变量值。这里后面解释的会更清晰。
  3. 第三个操作数newVal – 表示我们本次计算得到的新值。

执行一次CAS指令步骤
首先根据loc取到当前主存中变量的值val,将该值和oldVal比较,如果val == oldVal 则认为当前线程得出新值的过程中变量的值没有(实际上还存一种例外的情况,该值被其他线程更新多次又回到原值,这就是ABA问题)被其他线程更新过,所以直接将newVal写回loc所指的主存变量val;如果val != oldVal,则证明在当前线程在得出新值newVal的过程中,主存中变量值被其他线程修改过,所以本次就不要将newVal写回主存覆盖val了。

那么我们是怎么利用CAS操作实现操作的同步的呢?我们还是以i++;自增为例,我们去看看AtomicInteger是如何实现自增操作的原子性的,先贴出AtomicInteger自增的代码片段:

public final int incrementAndGet() {
   return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}

该方法实现的就是自增1操作,这里的this就是指我们继续跟进unsafe.getAndAddInt()方法,该方法是Unsafe类中的方法

public final int getAndAddInt(Object obj, long offset, int delta) {
    int expect;
    do {
    	// 取主存中变量值
        expect= this.getIntVolatile(obj, offset); 
        // CAS操作
    } while(!this.compareAndSwapInt(obj, offset, expect, expect + delta)); 
    return expect; // 返回expect
}

public final native boolean compareAndSwapInt(Object obj, long offset, int expect, int update);

正如我们看到的,我们在do-while循环里,根据obj和offset拿到主存中当前的值expect,然后执行compareAndSwapInt方法,如果expect和旧值相比较没有发生变化,compareAndSwapInt方法就会把新值expect+delta写回主存,!this.compareAndSwapInt(obj, offset, expect, expect + delta)就会返回false跳出循环。

这里值得注意的是在每次比较失败之后我们的旧值是会更新成本次获取到的主存中的值的。举个例子:假设有两个线程A和B。对于A线程,开始取出主存变量0到工作区,它自增0+1之后,想把1写回主存。它先看看主存中变量还是不是0,如果是0证明B线程没有修改该变量,我就放心把1写回主存。但是如果A观察到主存中的值已经为1了,证明B线程已经对变量的值修改了,这时A把1写回去就会丢失一次加1操作。所以此刻A不把1写回主存,而是再获取主存中的1,然后再执行一次自增1+1=2,自增后在看看主存中是1不?如果是1证明这次B线程总算没有在A自增时候对主存变量动手脚,所以A线程把2写回主存。

总结:getAndAddInt方法中,每次在想把新值写回主存时,都通过CAS操作观察主存中当前的值判断是否修改过,如果修改过就自旋,直到有一次观察到主存中的值没有被修改,才把本次更新的值写回主存。

AtomicInteger、AtomicLong、AtomicBoolean中的方法都与上述原理相似,就不一一说明了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值