铁锤的IT奇妙探险 Java Back-End Coder

CAS-无锁的线程安全

2020-07-16


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:新值

缺点

  1. ABA问题:具体解决在锁的了解中解释了
  2. 自旋时间过长,高并发场景下,CAS通常效率不高
  3. 线程安全的范围不能灵活控制,因为CAS是针对某一个共享变量

Similar Posts

Comments