CAS总结
一:概念
是一种乐观锁,可以实现无锁并发。因为没有使用synchronized,所以线程不会阻塞,效率更高。但是如果竞争激烈,重试必然发生,那么效率反而受影响。
二:原子类
Java中提供了一些原子类,底层就是使用CAS实现的,例如AtomicInteger、AtomicLong、AtomicReference、AtomicStampedReference等
例如AtomicInteger
提供了一个修改值的方法
atomicInteger.compareAndSet()
这个方法实际上是调用unsafe
的compareAndSwapInt
方法来保证原子性
public final boolean compareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update);}
其中valueOffset
代表当前值,expect
是预期值,update
是更改值
例如:我们使用compareAndSet(1,3)
,此时就会将expec的值1与现在当前对象实际的值valueOffset进行比较,
如果一致,将将当前值更改为update值3
三:ABA问题
以上原子类使用CAS确实可以达到原子性,但是此时有一个问题,就是主线程只能判断共享变量是否与最初的值相同,并不能判断是否被其他线程修改,例如此时另一个线程做了+1,-1的操作,值和原来一致。虽然这并不影响操作,但是如果希望只要别的线程进行了操作,CAS就失败,该怎么处理呢?
AtomicStampedReference
,通过版本号来判断是否被修改过,在修改时还需要判断版本号。并且版本号加1
四:LongAddr
LongAdder
是一个 Java 中的线程安全的累加器类型,它比原生的 AtomicLong 在高并发下更高效
,因为它把内部的值分为多个 Cell 来处理,并且也允许多个线程同时在不同的 Cell 上进行增加操作,从而避免了高并发环境下的竞争问题
关键域:
base
:基础值,如果没有竞争,则用CAS累加这个域
cells
: 累加单元数组,懒惰初始化
cellsBusy
:创建或者扩容时,置为1,表示加锁
源码
longAdder.increment();调用了add()
public void add(long x) {Cell[] as; long b, v; int m; Cell a;if ((as = cells) != null || !casBase(b = base, b + x)) { //cells是累加单元数组,此处判断是否为空,因为cells初始是nul,即此处判断是否有竞争//如果为空,就进入casBase()方法,对基础累加值进行累加,base就是基础值,此处是一个CAS操作。累加成功就不会进入下面的代码块boolean uncontended = true;if (as == null || (m = as.length - 1) < 0 || //如果cells不为空,代表以前发生过竞争 (a = as[getProbe() & m]) == null || //判断当前线程的cell是否创建,没有创建就进入longAccumulate创建!(uncontended = a.cas(v = a.value, v + x)))//如果已经创建,执行累加单元的CAS,如果成功,则返回,如果失败,执行下面longAccumulate(x, null, uncontended); // a.cas()失败,执行longAccumulate()}
}