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操作。
关注公众号:蜜蜂科技巢公众号了解更多知识
