AtomicXXX可以通过CAS(Compare And Set)机制进行原子操作。

但是存在ABA问题。

举例:

线程T1想修改100为101,

而在T1发出compareAndSet指令之前,有T2将100修改为了99,又改回了100,

此时T1发出compareAndSet指令,发现100还是100,复合条件,所以修改100为101。

然而此时已经不是之前的现场了。

问题代码:

        private static AtomicInteger atomicInt = new AtomicInteger(100);
				
        Thread intT1 = new Thread(new Runnable() {
            @Override
            public void run() {
                atomicInt.compareAndSet(100, 99);
                atomicInt.compareAndSet(99, 100);
            }
        });

        Thread intT2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                }
                boolean c3 = atomicInt.compareAndSet(100, 101);
                System.out.println(c3); // true
            }
        });

        intT1.start();
        intT2.start();
        intT1.join();
        intT2.join();

此时输出:

  true

可以通过AtomicStampedReference类增加版本号解决该问题,在设置值的同时加上期望的版本号和新的版本号,

代码如下:


			
        private static AtomicInteger atomicInt = new AtomicInteger(100);
        private static AtomicStampedReference atomicStampedRef = new AtomicStampedReference(100, 0);

        Thread refT1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                }
                atomicStampedRef.compareAndSet(100, 99, atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1);
                atomicStampedRef.compareAndSet(99, 100, atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1);
            }
        });

        Thread refT2 = new Thread(new Runnable() {
            @Override
            public void run() {
                int stamp = atomicStampedRef.getStamp();
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                }
                System.out.println(atomicStampedRef.getStamp());
                boolean c3 = atomicStampedRef.compareAndSet(100, 101, stamp, stamp + 1);
                System.out.println(c3); // false
            }
        });

        refT1.start();
        refT2.start();
        refT1.join();
        refT2.join();

输出如下

2
false

refT1中的sleep保证refT2可以获取修改前的stamp=1,

refT2中的sleep保证refT1能完成修改后再进行修改,

此时,refT2期望的stamp为0,但是此时获取的stamp已经是2,所以CAS返回失败。