CAS的实现
JAVA中的cas主要使用的是Unsafe方法,Unsafe的CAS操作主要是基于硬件平台的汇编指令,目前的处理器基本都支持CAS,只不过不同的厂家的实现不一样罢了。
Unsafe提供了三个方法用于CAS操作,分别是
public final native boolean compareAndSwapObject(Object value, long valueOffset, Object expect, Object update);
public final native boolean compareAndSwapInt(Object value, long valueOffset, int expect, int update);
public final native boolean compareAndSwapLong(Object value, long valueOffset, long expect, long update);
- value 表示 需要操作的对象
- valueOffset 表示 对象(value)的地址的偏移量(通过Unsafe.objectFieldOffset(Field valueField) 获取)
- expect 表示更新时value的期待值
- update 表示将要更新的值
具体过程为每次在执行CAS操作时,线程会根据valueOffset去内存中获取当前值去跟expect的值做对比如果一致则修改并返回true,如果不一致说明有别的线程也在修改此对象的值,则返回false。
ABA问题
线程1准备用CAS修改变量值A,在此之前,其它线程将变量的值由A替换为B,又由B替换为A,然后线程1执行CAS时发现变量的值仍然为A,所以CAS成功。但实际上这时的现场已经和最初不同了。
例子:
public static AtomicInteger a = new AtomicInteger(1);
public static void main(String[] args) throws InterruptedException {
Thread main = new Thread(() -> {
System.out.println("操作线程" + Thread.currentThread() +",初始值 = " + a); //定义变量 a = 1
try {
Thread.sleep(1000); //等待1秒 ,以便让干扰线程执行
} catch (InterruptedException e) {
e.printStackTrace();
}System.out.println("操作线程" + Thread.currentThread() +",CAS前初始值 = " + a);
boolean isCASSuccess = a.compareAndSet(1,2); // CAS操作
System.out.println("操作线程" + Thread.currentThread() +",CAS操作结果: " + isCASSuccess);
},"主操作线程");
Thread other = new Thread(() -> {
a.incrementAndGet(); // a 加 1, a + 1 = 1 + 1 = 2
System.out.println("操作线程" + Thread.currentThread() +",【increment】 ,值 = "+ a);
a.decrementAndGet(); // a 减 1, a - 1 = 2 - 1 = 1
System.out.println("操作线程" + Thread.currentThread() +",【decrement】 ,值 = "+ a);
},"干扰线程");
main.start();
other.start();
main.join();
System.out.println("a = " + a);
}
输出:
操作线程Thread[主操作线程,5,main],初始值 = 2
操作线程Thread[干扰线程,5,main],【increment】 ,值 = 2
操作线程Thread[干扰线程,5,main],【decrement】 ,值 = 1
操作线程Thread[主操作线程,5,main],CAS前初始值 = 1
操作线程Thread[主操作线程,5,main],CAS操作结果: true
a = 2
解决ABA
解决ABA最简单的方案就是给值加一个修改版本号,每次值变化,都会修改它版本号,CAS操作时都对比此版本号。AtomicStampedReference
主要维护包含一个对象引用以及一个可以自动更新的整数”stamp”的pair对象来解决ABA问题。
private static AtomicStampedReference atomicStampedRef =
new AtomicStampedReference(1, 0);
public static void main(String[] args) throws InterruptedException {
Thread main = new Thread(() -> {
System.out.println("操作线程" + Thread.currentThread() +",初始值 a = " + atomicStampedRef.getReference());
int stamp = atomicStampedRef.getStamp(); //获取当前标识别
try {
Thread.sleep(1000); //等待1秒 ,以便让干扰线程执行
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean isCASSuccess = atomicStampedRef.compareAndSet(1,2,stamp,stamp +1); //此时expectedReference未发生改变,但是stamp已经被修改了,所以CAS失败
System.out.println("操作线程" + Thread.currentThread() +",CAS操作结果: " + isCASSuccess);
},"主操作线程");
Thread other = new Thread(() -> {
atomicStampedRef.compareAndSet(1,2,atomicStampedRef.getStamp(),atomicStampedRef.getStamp() +1);
System.out.println("操作线程" + Thread.currentThread() +",【increment】 ,值 = "+ atomicStampedRef.getReference());
atomicStampedRef.compareAndSet(2,1,atomicStampedRef.getStamp(),atomicStampedRef.getStamp() +1);
System.out.println("操作线程" + Thread.currentThread() +",【decrement】 ,值 = "+ atomicStampedRef.getReference());
},"干扰线程");
main.start();
other.start();
main.join();
System.out.println("操作线程" + Thread.currentThread() +",结果 a = " + atomicStampedRef.getReference());
}
输出:
操作线程Thread[主操作线程,5,main],初始值 a = 2
操作线程Thread[干扰线程,5,main],【increment】 ,值 = 2
操作线程Thread[干扰线程,5,main],【decrement】 ,值 = 1
操作线程Thread[主操作线程,5,main],CAS操作结果: false
操作线程Thread[main,5,main],结果 a = 1