CAS机制
CAS (Compare And Swap)比较并替换,CAS的三个操作数:内存地址(V)、预期值(A)、替换值(B)。
CAS操作过程:内存地址V上的值与A值相等则用B替换A值返回true,不相等返回false。
CAS指令利用JNI来完成非阻塞算法,因为java.unit.concurrent包是建立在CAS机制上的,所以相对于synchronized来说concurrent
的效率非常高。
CAS存在问题:
ABA问题:CAS要判断操作值是否发生了变化,如果操作值由A变为B,又由B变为A则CAS无法判断正确。解决ABA问题的方法是加版本号,即使值由A到B再到A,两个A的版本号不同,CAS可以判断出值是否变化。
循环时间长开销大:自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销。
只能保证一个共享变量的原子操作:当对一个共享变量(volatile修饰)执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,例如共享变量a和b,CAS变量a为true,CAS变量b为false但是此时a已经被替换,所以循环CAS无法保证多个共享变量的原子性,这个时候就可以用锁,或者把多个共享变量合并成一个共享变量来操作。将多个变量放到一个对象里来操作。
Unsafe类
java中CAS机制是通过sun.misc.Unsafe.compareAndSwapObject(Object var1, long var2, Object var4, Object var5)实现的。
ConcurrentHashMap中Node的CAS操作
//tab的CAS操作
//为什么偏移量是((long)i << ASHIFT) + ABASE
//ABASE是tab对ConcurrentHashMap的偏移量,ASHIFT是第一个Node对tab的偏移量
static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i, Node<K,V> c, Node<K,V> v) {
return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
}
static {
try {
U = sun.misc.Unsafe.getUnsafe();
Class<?> ak = Node[].class;
ABASE = U.arrayBaseOffset(ak);
int scale = U.arrayIndexScale(ak);
if ((scale & (scale - 1)) != 0)
throw new Error("data type scale not a power of two");
ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
} catch (Exception e) {
throw new Error(e);
}
}