Java,CAS中的ABA问题及解决方法

304 阅读1分钟

在Hesey的《用ATOMICSTAMPEDREFERENCE解决ABA问题》疑问中,使用了一个非常具有表达力的例子。

  • 有一个栈(单链表实现)如下所示:

    a.next = b, b.next = null

    栈顶元素为A,现在Thread1准备将栈顶元素变为B,而在此时Thread2执行了以下操作,将A、B出栈,并且将A、C、D入栈,此时栈为

    此时线程发现栈顶为A(但是此时的A已经不是原来的A),于是Thread1将栈顶元素改为b ,此时结构为
    C、D丢失,栈中只有元素B

如何解决?

在java中,使用AtomicStampedRefrece进行版本戳标记 对应上例中初始状态A1.next = B2, 线程2更新后,C4.next = D3, A5.next = C4 线程1工作,A5 != A1,更新失败。

代码示例

以下代码分别演示了AtomicInteger和AtomicStampedReference的1结果。

class Test{
    private static AtomicInteger atomicInt = new AtomicInteger(100);
    private static AtomicStampedReference atomicStampedRef = new AtomicStampedReference(100, 0);
    public static void main(String[] args) throws InterruptedException {
        Thread intT1 = new Thread(new Runnable() {
            @Override
            public void run() {
                atomicInt.compareAndSet(100, 101);
                atomicInt.compareAndSet(101, 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
                System.out.println(atomicInt); // 101
            }
        });
        intT1.start();
        intT2.start();
        intT1.join();
        intT2.join();
        System.out.println("==============");
        Thread refT1 = new Thread(new Runnable() {
            @Override
            public void run(){
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                  System.out.println("****");
                }
                atomicStampedRef.compareAndSet(100, 101, atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1);
                atomicStampedRef.compareAndSet(101, 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) {
            }
            boolean c3 = atomicStampedRef.compareAndSet(100, 101, stamp, stamp + 1);
            System.out.println(c3); // false
            System.out.println(atomicStampedRef.getReference()); // 100, CAS失败
        }
    });
       refT1.start();
       refT2.start();
       refT1.join();
       refT2.join();
}
}