CAS原子操作

前言

CAS是一种乐观锁思想的应用,所以在介绍CAS之前,需要先了解一下何为乐观锁、何为悲观锁?

乐观锁与悲观锁

众所周知,CPU是时分复用的。也就是把CPU的时间片,分配给不同的thread/process轮流执行。时间片之间需要进行CPU切换,也就是会发生进程的切换。这里的切换,会涉及到清空寄存器、缓存数据,然后重新加载新的thread所需数据。当一个线程被挂起时,加入到阻塞队列,在一定的时间、条件下,通过notify()/notifyAll()唤醒回来。在某个资源不可用的时候,就把CPU让出,同时把当前线程切换到阻塞状态。等到资源可用了,就把线程唤醒,让他进入runnable状态,供CPU调度。这就是一个典型的悲观锁。synchronized是一种独占锁,即悲观锁。它假设在最坏的情况下,一个线程修改共享数据的时候其他线程也会修改该数据。因此只在确保其他线程不会造成干扰的前提下执行,会导致其他所有需要锁的线程挂起,等待持有锁的线程释放锁。
然而,线程的挂起、恢复需要很大的开销。当一个线程正在等待锁时,它不能干其他事,体现出了悲观锁的缺点。例如,一个线程需要某个资源,但是该资源占有时间极短。当一个线程首次抢占时,发现该资源正在被占用。线程挂起时,发现该资源可能立即可用,然后还得去花大把时间去重新抢占锁。如此一来一往,付出的时间代价如此之大。
正因为悲观锁的缺点所在,于是才有了乐观锁。核心思路:每次不加锁,而是假设修改数据之前,其他线程一定不会修改。如果因为被修改过而造成的冲突、失败,就重试,直到成功为止。站在乐观锁的角度,线程没必要让出CPU,可以一直while循环,失败就重试直到成功。所以,数据争抢不严重时,乐观锁是不二的选择。

重新认识CAS

CAS操作包含3个操作数:内存位置V、预期原值A、新值B。执行CAS操作时,需要比较V、A,若匹配,处理器就会自动把V更新为B。否则,不做处理。
比如,一个线程需要修改共享变量的值,要先取出共享变量的值赋给A,基于A进行计算得到B。需要更新共享变量的值了,可以调用CAS方法更新。
Java中可以通过锁和循环CAS方式实现原子操作。java.util.concurrent.atomic包相关类就是CAS实现。
通过Atomiclneger源码来分析:

public final int getAndAdd(int delta){
        for(;;){
            int current = get();
            int next = current+delta;
            if (compareAndSet(current,next))
                return current;
        }
    }

每次都从内存读取数据,+1操作,然后2个值进行CAS操作。成功就返回,失败就重试直至成功。

public final boolean compareAndSet(int expect,int update){
        return unsafe.compareAndSwapInt(this,valueOffset,expect,update);
    }

compareAndSet又调用的compareAndSwapInt方法,借助C来调用CPU底层指令来保证在硬件层面上实现原子操作。在intel处理器中,CAS就是通过compxchg指令完成的。这就是CAS原子操作(compare and swap)。

CAS的问题

1.ABA问题

因为CAS需要在操作值的时候,检查值有没有发生变化,没有发生变化才去更新。但是如果一个值原来是A变成了B,又变成了A,CAS检查会判断该值未发生变化,实际却变化了。
解决思路:增加版本号,每次变量更新时把版本号+1,A-B-A就变成了1A-2B-3A。JDK5之后的atomic包提供了AtomicStampedReference来解决ABA问题,它的compareAndSet方法会首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志。全部相等,才会以原子方式将该引用、该标志的值设置为更新值。

2.循环时间长、开销大

自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销。

3.只能保证一个共享变量的原子操作

对一个共享变量执行操作时,可以循环CAS方式确保原子操作。但是对多个共享变量,就不灵了。这里可以使用锁,或把多个共享变量合并为1个共享变量,如i=2,j=a,合并为ij=2a。然后用CAS操作ij。在JDK5后,提供了AtomicReference类来保证对象间的原子性,可以把多个共享变量放在一个对象里进行CAS操作。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值