CAS
CAS在Java中的全称是CompareAndSwap(比较和交换),是一种无锁算法。java.util.concurrent包中的原子类通过CAS实现了乐观锁,实现多线程间不使用锁(不阻塞线程)的变量同步。
基本介绍
CAS运算包含三个操作数——内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期的原始值匹配,处理器会自动用新值更新该位置的值。否则,处理器什么也不做。
使用场景
有时候我们需要对变量进行操作。如果是多线程的话,可能达不到我们预期的效果。Synchronized等关键字可以解决问题,但是Synchronized关键字会使没有得到锁资源的线程进入BLOCKED状态,竞争后又回到RUNNABLE状态锁资源。这个过程涉及到操作系统用户态和内核态公众号的转换,成本比较高。这时候我们可以使用Atomic的一些原子操作。
公开课CASTest{ publicstaticvolatileintcount=0; publicstaticAtomicIntegeratomicCount=newAtomicInteger(); publicstaticvoidmain(String[]args)抛出InterruptedException{ 对于(inti=0;i<2;i++){ 新线程(()->{ 对于(intj=0;j<10000;j++){ 计数++; 原子计数。增量获取(); } System.out.println("计数++结束!"); })。开始(); } 线。睡眠(2000); System.out.println(计数); System.out.println(atomicCount.get()); } }看这个count++结束的代码输出! 计数++结束! 18003 20000 可以看到volatile没办法保证操作的原子性。AtomicInteger使用CAS操作来保证操作的原子性。AtomicInteger的实现原理
我们可以看看AtomicInteger中incrementAndGet()的源码
publicfinalintincrementAndGet(){ 返回不安全。getAndAddInt(this,valueOffset,1)+1; }是通过Unsafe实现的,我们进入Unsafe中的getAndInt()方法
publicfinalintgetAndAddInt(Objectvar1,longvar2,intvar4){ 变量5; 做{ var5=这个。getIntVolatile(var1,var2); }while(!this.compareAndSwapInt(var1,var2,var5,var5+var4)); 返回var5; }compareAndSwapInt是一个本地方法,它使用CPU的cmpxchg指令来比较寄存器中的值A和内存中的值V。如果相等,则将要写入的新值B存入内存。如果不相等,则将内存值V赋值给寄存器中的值A。然后在Java代码中通过while循环再次调用cmpxchg指令重试,直到设置成功。
CAS问题
- ABA问题CAS在操作数值的时候需要检查内存值是否发生了变化,如果没有发生变化就会更新内存值。但是如果内存值本来是A,然后改成B,再改成A,那么CAS检查的时候,会发现这个值没有变,但实际上变了。ABA问题的解决方法是在变量前加一个版本号,变量每更新一次版本号就加一,这样变化过程就从“A-B-A”变为“1A-2B-3A”.JDK从1.5开始提供了AtomicStampedReference类来解决ABA问题。
- 当循环时间长,开销大时,并发度就高。CAS操作长时间失败会一直自旋,给CPU带来非常大的开销。
- 只能保证一个共享变量的原子操作对一个共享变量进行操作时,CAS可以保证原子操作,但是对多个共享变量进行操作时,CAS不能保证操作的原子性。
Java从1.5开始,JDK提供了AtomicReference类来保证引用对象之间的原子性,可以将多个变量放在一个对象中进行CAS操作。
关注公众号:蜜蜂科技巢公众号了解更多知识