一.JUC多线程及并发包
1.3.原子类AtomicInteger的ABA问题谈谈?原子更新引用知道吗
ABA问题分析
CAS—->Unsafe类--->CAS底层思想—>ABA—->原子引用更新—->如何规避ABA问题
ABA问题: 狸猫换太子, 在中途的时候 值被更换了,然后又换回来了
A—>B—->A 没人发现!
当线程A操作资源类的时候,访问资源类是时候假设为A,然后他就去sleep了,这个时候线程B来操作了,资源类从A变成了B,然后进行了一系列业务逻辑,然后线程C又来了,发现资源类现在成了B,是自己想要的,又操作一遍,这个时候线程C操作完以后,将资源类从B又变成A, 而线程A醒来傻傻的还没有发现。
如何解决ABA
-
采用时间戳来解决原子引用问题或者版本号
AtomicStampedReference
类解决该方法 -
采用
AtomicReference
操作 包装POJO对象AtomicReference
案例代码
class User{
private int age;
private String name;
public User(int age,String name){
this.age=age;
this.name=name;
}
}
public class AtomicInteger_1_3 {
public static void main (String[] args) {
User user1=new User(12, "zs");
User user2=new User(18, "li");
AtomicReference<User> userAtomicReference =new AtomicReference <>();
userAtomicReference.set(user1);
System.out.println(userAtomicReference.compareAndSet(user1, user2));
System.out.println(userAtomicReference.compareAndSet(user1, user2));
}
}
结果分析:
AtomicStampedReference
版本号Case
package com.ybzn._01.juc;
/**
* @author Hugo
* @time 2021/2/13
*/
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;
/**
* Description: ABA问题的解决 *
*/
public class ABADemo_1_3 {
private static AtomicReference <Integer> atomicReference = new AtomicReference <>(100);
private static AtomicStampedReference <Integer> stampedReference = new AtomicStampedReference <>(100, 1);
public static void main (String[] args) {
System.out.println("===以下是ABA问题的产生===");
new Thread(() -> {
atomicReference.compareAndSet(100, 101);
atomicReference.compareAndSet(101, 100);
}, "t1").start();
new Thread(() -> { //先暂停1秒 保证完成ABA
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicReference.compareAndSet(100, 2019) + "\t" + atomicReference.get());
}, "t2").start();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("===以下是ABA问题的解决===");
new Thread(() -> {
int stamp = stampedReference.getStamp();
System.out.println(Thread.currentThread().getName() + "\t 第1次版本号" + stamp + "\t值是" + stampedReference.getReference()); //暂停1秒钟t3线程
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
stampedReference.compareAndSet(100, 101, stampedReference.getStamp(), stampedReference.getStamp() + 1);
System.out.println(Thread.currentThread().getName() + "\t 第2次版本号" + stampedReference.getStamp() + "\t值是" + stampedReference.getReference());
stampedReference.compareAndSet(101, 100, stampedReference.getStamp(), stampedReference.getStamp() + 1);
System.out.println(Thread.currentThread().getName() + "\t 第3次版本号" + stampedReference.getStamp() + "\t值是" + stampedReference.getReference());
}, "t3").start();
new Thread(() -> {
int stamp = stampedReference.getStamp();
System.out.println(Thread.currentThread().getName() + "\t 第1次版本号" + stamp + "\t值是" + stampedReference.getReference()); //保证线程3完成1次ABA
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean result = stampedReference.compareAndSet(100, 2019, stamp, stamp + 1);
System.out.println(Thread.currentThread().getName() + "\t 修改成功否" + result + "\t最新版本号" + stampedReference.getStamp());
System.out.println("最新的值\t" + stampedReference.getReference());
}, "t4").start();
}
}
结果分析: