CAS如何通过无锁的方式来保证线程安全?乐观锁的原理是什么?
CAS
当多个线程同时使用 CAS 更新同一个变量时,只有其中一个线程能够操作成功,而其他线程都会更新失败。不过和同步互斥锁不同的是,更新失败的线程并不会被阻塞,而是被告知这次由于竞争而导致的操作失败,但还可以再次尝试。
原理
CAS的等价代码如下
public class SimulatedCAS {
private int value;
public synchronized int compareAndSwap(int expectedValue, int newValue) {
int oldValue = value;
if (oldValue == expectedValue) {
value = newValue;
}
return oldValue;
}
}
如果预期值等于当前内存值,则将内存值修改为新值,用synchronized可以保证操作原子性。
应用
ConcurrentHashMap
putVal()方法
if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value, null)))
break;
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);
}
casTabAt
调用了compareAndSwapObject
方法,以及ConcurrentHashMap的merge
,compute
等方法中都有casTabAt
的应用。
原子类
如AtomicInteger
的 getAndAdd
方法
public final int getAndAdd(int delta) {
return unsafe.getAndAddInt(this, valueOffset, delta);
}
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
//这是个死循环,会一直执行直到更新成功
return var5;
}
//var1:要修改的对象;var2:offset(偏移量)
//var5:期望值;var5+var4:新值
缺点
- ABA问题:具体解决在锁的了解中解释了
- 自旋时间过长,高并发场景下,CAS通常效率不高
- 线程安全的范围不能灵活控制,因为CAS是针对某一个共享变量