一.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认为和自己的值相同,则又进行了操作。
二.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版本号原子引用
线程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();
}