JDK对CAS ABA问题解决-AtomicMarkableReference和AtomicStampedReference

我们知道AtomicInteger和AtomicLong的原子操作,但是在这两个类在CAS操作的时候会遇到ABA问题,可能大家会疑问什么是ABA问题呢,请待我细细道来:

ABA问题:简单讲就是多线程环境,2次读写中一个线程修改A->B,然后又B->A,另一个线程看到的值未改变,又继续修改成自己的期望值。当然我们如果不关心过程,只关心结果,那么这个就是无所谓的ABA问题。

  • 为了解决ABA问题,伟大的java为我们提供了AtomicMarkableReference和AtomicStampedReference类,为我们解决了问题
  • AtomicStampedReference是利用版本戳的形式记录了每次改变以后的版本号,这样的话就不会存在ABA问题了,在这里我借鉴一下别人举得例子

举个通俗点的例子,你倒了一杯水放桌子上,干了点别的事,然后同事把你水喝了又给你重新倒了一杯水,你回来看水还在,拿起来就喝,如果你不管水中间被人喝过,只关心水还在,这就是ABA问题。如果你是一个讲卫生讲文明的小伙子,不但关心水在不在,还要在你离开的时候水被人动过没有,因为你是程序员,所以就想起了放了张纸在旁边,写上初始值0,别人喝水前麻烦先做个累加才能喝水。这就是AtomicStampedReference的解决方案。

  • 我写了个代码,大家请看
import java.util.concurrent.atomic.AtomicStampedReference;
public class AtomicStampedReferenceTest {
    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference(0, 0);
    public static void main(String[] args) throws InterruptedException {
        final int stamp = atomicStampedReference.getStamp();
        final Integer reference = atomicStampedReference.getReference();
        System.out.println(reference+"============"+stamp);
        Thread t1 = new Thread(new Runnable() {
        @Override
        public void run() {
                System.out.println(reference + "-" + stamp + "-"
                + atomicStampedReference.compareAndSet(reference, reference + 10, stamp, stamp + 1));
        }
        });

        Thread t2 = new Thread(new Runnable() {
        @Override
        public void run() {
                Integer reference = atomicStampedReference.getReference();
                System.out.println(reference + "-" + stamp + "-"
                + atomicStampedReference.compareAndSet(reference, reference + 10, stamp, stamp + 1));
        }
        });
        t1.start();
        t1.join();
        t2.start();
        t2.join();

        System.out.println(atomicStampedReference.getReference());
        System.out.println(atomicStampedReference.getStamp());
    }
}

输出结果:

0============0
0-0-true
10-0-false
10
1

结论:可以看出第二次更新的时候,失败了,因为版本号已经被改变了,所以AtomicStampedReference帮我们解决了CAS操作中的ABA问题!

AtomicMarkableReference跟AtomicStampedReference差不多, 
AtomicStampedReference是使用pair的int stamp作为计数器使用,AtomicMarkableReference的pair使用的是boolean mark。 
还是那个水的例子,AtomicStampedReference可能关心的是动过几次,AtomicMarkableReference关心的是有没有被人动过,方法都比较简单。

posted @ 2018-04-01 18:24  BarryW  阅读(1989)  评论(0编辑  收藏  举报