媒介
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上):