CAS的ABA问题及其解决

何为CAS的ABA问题?

一个线程T1从内存位置V中取出A,这时候另一个线程T2也从内存中取出A,并且T2进行了一些操作变成了B,然后two又将V位置的数据变成A,这时候线程T1进行CAS操作发现内存中仍然是A,然后T1操作成功。尽管线程T1的CAS操作成功,但是不代表这个过程就是没有问题的。
在这里插入图片描述

ABA问题的根本在于CAS在修改变量的时候,无法记录变量的状态,比如修改的次数,是否修改过这个变量。这样就很容易在一个线程将A修改成B时,又再次将B修改成A,造成CAS多次执行的问题。

代码模拟ABA问题

public class ABADemo {

    static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
    
    public static void main(String args[]) {
        System.out.println("模拟ABA问题:");
        new Thread(() -> {
            // ABA
            atomicReference.compareAndSet(100, 101);
            atomicReference.compareAndSet(101, 100);
        }, "T1").start();

        new Thread(() -> {
            try {
                // 暂停一秒钟,保证T1线程干完所有事情
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(atomicReference.compareAndSet(100, 2019) + " 当前" + atomicReference.get());
        }, "T2").start();
	}
}

在这里插入图片描述

如何解决ABA问题?

使用AtomicStampedReference的compareAndSet函数,这里面有四个参数:

compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp)
(1)第一个参数expectedReference:表示预期值。
(2)第二个参数newReference:表示要更新的值。
(3)第三个参数expectedStamp:表示预期的时间戳。
(4)第四个参数newStamp:表示要更新的时间戳。

public class ABADemo {

    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1);

    public static void main(String args[]) {
        new Thread(() -> {
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + " 第一次版本号:" + stamp);
            try {
                // 暂停一秒钟T3,等待T4拿到相同的版本号
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // ABA
            atomicStampedReference.compareAndSet(100, 101, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
            System.out.println(Thread.currentThread().getName() + " 第二次版本号:" + atomicStampedReference.getStamp());

            atomicStampedReference.compareAndSet(101, 100, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
            System.out.println(Thread.currentThread().getName() + " 第三次版本号:" + atomicStampedReference.getStamp());
        }, "T3").start();

        new Thread(() -> {
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + " 第一次版本号:" + stamp);
            try {
                // 暂停三秒钟T4
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            boolean result = atomicStampedReference.compareAndSet(100, 2019, stamp, stamp + 1);
            System.out.println("当期T4的版本号:" + stamp);
            System.out.println("当期主存的真实版本号:" + atomicStampedReference.getStamp());
            System.out.println(Thread.currentThread().getName() + "修改成功否:" + result + " 当前值:" + atomicStampedReference.getReference());
        }, "T4").start();
    }
}

在这里插入图片描述

GitHub:多线程相关Demo

发布了83 篇原创文章 · 获赞 23 · 访问量 3524

猜你喜欢

转载自blog.csdn.net/qq_44779506/article/details/105539767
今日推荐