AtomicXXX中的ABA问题

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返回失败。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值