一.ABA问题的产生

二.AtomicReference原子引用

三.AtomicStampedReference版本号原子引用

四.ABA问题解决

前面的内容之间的关联:CAS->Unsafe类->CAS思想->ABA问题->如何解决ABA问题

一.ABA问题的产生(狸猫换太子)

1)线程1和线程2开启时,按照之前讲述的对线程变量的操作,把主内存的值A复制到线程中的工作内存A

2)线程1需要10s,线程2需要2s,假设线程2先修改则线程2中的工作内存的值A和主内存中的值A修改为B

3)等待线程1的过程中,线程2又把自己内存中的值和主内存中的值修改为“A”

4)此时线程1开启,发现线程1中的A与主内存中的“A”相同,按照CAS的方法把值修改为B

简单的来说就是由于线程1和线程2存在时间差,线程2执行完之后又执行了一次改回来”原来的“值,线程1认为和自己的值相同,则又进行了操作。

java是如何解决aba问题的 aba java 导致问题_java

 二.AtomicReference原子引用

public class AtomicReferenceDemo {
    public static void main(String[] args) {
        User lzm = new User("lzm", 18);
        User lin = new User("lin", 20);
        AtomicReference<User> atomicReference = new AtomicReference<>();
        atomicReference.set(lzm);
        System.out.println(atomicReference.compareAndSet(lzm, lin)); // true
        System.out.println(atomicReference.get()); // User(userName=lin, age=20)
    }
}
class User{
    String userName;
    int age;
}

 三.AtomicStampedReference版本号原子引用

java是如何解决aba问题的 aba java 导致问题_java是如何解决aba问题的_02

 线程A和线程B初始值都为100,版本号都为1,假设线程A先执行,此时线程B挂起,线程A的值把100修改为101,此时版本号加1变为2,由于时间差,线程A又把值101修改为100,此时版本号继续加1变为3,主内存的值为“100”;此时线程B开启,线程B工作内存的值100与主内存中的“100”对比发现相同则进行修改操作,此时版本号变为2.这个过程中虽然修改成功但是线程B和主内存中100并非相同,主内存的值被狸猫换太子了,通过版本号就可清晰看出。

 四.ABA问题解决

增加版本号

public class ABADemo2 {
    private static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1);

    public static void main(String[] args) {
        new Thread(() -> {
            //获得版本号为1
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + " 的版本号为:" + stamp);
            //睡眠1s是为了执行下面的一个线程使得版本号都为1开始
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //100修改为101再修改为100 产生ABA问题 每次修改版本号加1
            atomicStampedReference.compareAndSet(100, 101, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1 );//版本号为2
            atomicStampedReference.compareAndSet(101, 100, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1 );//版本号为3
        }).start();

        new Thread(() -> {
            //获得版本号为1
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + " 的版本号为:" + stamp);
            //睡眠3s是为了让上面的线程先执行完 产生ABA
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            由于上面的线程版本号为3 这个线程版本号为2 所以返回false
            boolean b = atomicStampedReference.compareAndSet(100, 2020, stamp, stamp + 1);
            System.out.println(b); // false
            System.out.println(atomicStampedReference.getReference()); // 100
        }).start();
    }