原子操作CAS
1、CAS的基本原理
利用了现代处理器都支持的CAS的指令,循环这个指令,直到成功为止
CAS(Compare And Swap),指令级别保证了这是一个原子操作(指操作不会被线程调度机制打断)
它有3个操作数:内存值V,预期值A,更新值B,如果内存值V和期望值A相等,就把V修改为B,循环CAS操作,直到成功为止。
cas自旋.png
以AtomicInteger为例:
//++i
public final int incrementAndGet() {
return U.getAndAddInt(this, VALUE, 1) + 1;
}
//i++
public final int getAndIncrement() {
return U.getAndAddInt(this, VALUE, 1);
}
//额外提供了compareAndSet方法,第一个期望值,第二个希望修改成的值
public final boolean compareAndSet(int expect, int update) {
return U.compareAndSwapInt(this, VALUE, expect, update);
}
//Unsafe类
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
////compareAndSwapInt是一个native方法,CPU原语指令,是连续执行不会被打断的,所以可以保证原子性。
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
AtomicInteger由硬件提供原子操作指令实现的。在非激烈竞争的情况下,开销更小,速度更快。
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(6);
//true
System.out.println(atomicInteger.compareAndSet(6,66));
//false
System.out.println(atomicInteger.compareAndSet(6,66));
}
AtomicInteger的compareAndSet方法就是CAS操作:
初始值6,调用 compareAndSet(6,66),期望值为6(相等),主内存中的值被修改为66,输出true;
第2次调用 compareAndSet(6,66),期望值为6,但此时主内存中的值为66,输出false。
2、CAS的问题
-
ABA问题
变量A,修改为变量B,然后又修改为A,实际上已经修改过了。
对于这个问题,每次修改加上类似版本戳的方式,JDK提供了:
AtomicMarkableReference:是否修改过
AtomicStampedReference:改过几次
-
开销问题
自旋CAS不成功,就会一直循环执行,直到成功,如果长时间不成功,会给CPU带来大的执行开销。
-
只能保证一个共享变量的原子操作
这个问题可以采用合并共享变量的方式处理,JDK提供了AtomicReference类来保证引用对象之间的原子性,就可以把多个变量放在一个对象里来进行CAS操作
网友评论