变量初始值为A,修改成B,通过AtomicReference的compareAndSet去改变。就会先比较原来的值是否为A,如果为A则修改成功,否则修改失败。
但是如果在这个过程中,A变成D然后又变成A, 那么再调用compareAndSet去改变也能修改成功B。这个过程中A->D->A,程序是感知不到的。
@Slf4j
public class ABADemo {static AtomicReference<String> atomicReference = new AtomicReference<>("A");public static void main(String[] args) {String prev = atomicReference.get();//中间A->B->Aother();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}atomicReference.compareAndSet(prev, "C");log.info("atomicReference===={}", atomicReference.get());}public static void other() {new Thread(() -> {atomicReference.compareAndSet(atomicReference.get(), "B");log.info("atomicReference===={}", atomicReference.get());}, "t1").start();try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {atomicReference.compareAndSet(atomicReference.get(), "A");log.info("atomicReference===={}", atomicReference.get());}, "t2").start();}
}
上面的代码:主线程仅能判断共享变量的值是否与最初的A是否相同,不同感知这种从A变成了B,又变成了A的情景。
需求
如果主线程希望,只要有其他线程动过这个共享变量,那么自己cas操作就算失败,这时比较值是不够的,需要加入版本号。
谁做了修改,让这个版本号加一。
AtomicStampedReference 时间戳或者版本号
@Slf4j
public class ABADemo {//第一个此参数是值,第二个参数是版本号static AtomicStampedReference<String> atomicReference = new AtomicStampedReference<>("A", 1);public static void main(String[] args) {String prev = atomicReference.getReference();//版本号int stamp = atomicReference.getStamp();//中间A->B->Aother();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}//旧的值,新的值,旧的版本号,新的版本号(+1)boolean res = atomicReference.compareAndSet(prev, "C", stamp, stamp + 1);log.info("更新结果={},atomicReference===={}", res, atomicReference.getReference());}public static void other() {new Thread(() -> {//获取当前数据的版本号int stamp = atomicReference.getStamp();boolean res = atomicReference.compareAndSet(atomicReference.getReference(), "B", stamp, stamp+1);log.info("更新结果={},atomicReference===={}", res, atomicReference.getReference());}, "t1").start();try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {//获取当前数据的版本号int stamp = atomicReference.getStamp();boolean res = atomicReference.compareAndSet(atomicReference.getReference(), "A", stamp, stamp+1);log.info("更新结果={},atomicReference===={}", res, atomicReference.getReference());}, "t2").start();}
}
通过AtomicStampedReference的版本号,可以知道数据改了多少次。但是有时候我们不关心数据改了多少次,只是想知道数据中间有没有改变过。所以就可以使用AtomicMarkableReference。
@Slf4j
public class Test01 {public static void main(String[] args) {GarbageBag garbageBag = new GarbageBag("装满了垃圾");//第二个参数作为一个标记,true表示垃圾袋满了AtomicMarkableReference<GarbageBag> atomicMarkableReference = new AtomicMarkableReference<>(garbageBag, true);log.info("start....");GarbageBag prev = atomicMarkableReference.getReference();log.info("prev==={}", prev);//保洁阿姨去换了空垃圾袋new Thread(() -> {boolean res = atomicMarkableReference.compareAndSet(prev, new GarbageBag("空垃圾袋"), true, false);log.info("res={}, garbageBag={}", res, atomicMarkableReference.getReference());}, "保洁阿姨").start();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}//换空垃圾袋失败,因为保洁阿姨已经换了,标记为false,垃圾袋空了log.info("换一个新的垃圾袋");boolean res = atomicMarkableReference.compareAndSet(prev, new GarbageBag("空垃圾袋"), true, false);log.info("res={}, garbageBag={}", res, atomicMarkableReference.getReference());}
}class GarbageBag {private String desc;public GarbageBag(String desc) {this.desc = desc;}public void setDesc(String desc) {this.desc = desc;}@Overridepublic String toString() {return super.toString()+"----"+desc;}
}