媒介
CAS(Compare and Swap),即较量并替换,实现并发算法时常用到的一种技能,Doug lea大神在java同步器中大量利用了CAS技能,巧夺天工的实现了多线程执行的安详性。
CAS的思想很简朴:三个参数,一个当前内存值V、旧的预期值A、即将更新的值B,当且仅当预期值A和内存值V沟通时,将内存值修改为B并返回true,不然什么都不做,并返回false。
问题
一个n++的问题。
public class Case { public volatile int n; public void add() { n++; } }
通过javap -verbose Case看看add要领的字节码指令
public void add(); flags: ACC_PUBLIC Code: stack=3, locals=1, args_size=1 0: aload_0 1: dup 2: getfield #2 // Field n:I 5: iconst_1 6: iadd 7: putfield #2 // Field n:I 10: return
n++被拆分成了几个指令:
通过volatile修饰的变量可以担保线程之间的可见性,但并不能担保这3个指令的原子执行,在多线程并发执行下,无法做到线程安详,获得正确的功效,那么应该如何办理呢?
如何办理
在add要领加上synchronized修饰办理。
public class Case { public volatile int n; public synchronized void add() { n++; } }
这个方案虽然可行,可是机能上差了点,尚有其它方案么?
再来看一段代码
public int a = 1; public boolean compareAndSwapInt(int b) { if (a == 1) { a = b; return true; } return false; }
假如这段代码在并发下执行,会产生什么?
假设线程1和线程2都过了a==1的检测,都筹备执行对a举办赋值,功效就是两个线程同时修改了变量a,显然这种功效是无法切合预期的,无法确定a的最终值。
办理要领也同样暴力,在compareAndSwapInt要领加锁同步,酿成一个原子操纵,同一时刻只有一个线程才气修改变量a。
除了低机能的加锁方案,我们还可以利用JDK自带的CAS方案,在CAS中,较量和替换是一组原子操纵,不会被外部打断,且在机能上更占有优势。
下面以AtomicInteger的实现为例,阐明一下CAS是如何实现的。
public class AtomicInteger extends Number implements java.io.Serializable { // setup to use Unsafe.compareAndSwapInt for updates private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } } private volatile int value; public final int get() {return value;} }
看看AtomicInteger如何实现并发下的累加操纵:
public final int getAndAdd(int delta) { return unsafe.getAndAddInt(this, valueOffset, delta); } //unsafe.getAndAddInt 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; }
假设线程A和线程B同时执行getAndAdd操纵(别离跑在差异CPU上):