1、原子引用
1.1 AtomicReference
java.util.concurrent.atomic包中不仅有原子基本类型像AtomicInteger、AtomicBoolean等基本原子数据类型。
如果我们有像User类、Org类这种引用数据类型,那么JUCA包中也为我们提供了AtomicReference原子引用。具体使用如下。
User u = new User();
//原子引用
AtomicReference<User> atomicReference = new AtomicReference<User>(u);
这样我们便可获得一个原子User类atomicReference.
如下demo便可以实现User对象操作的原子性。但是还是会存在ABA问题。
public class UnsafeJH {
static User u = new User();
//原子引用
static AtomicReference<User> atomicReference = new AtomicReference<User>(u);
public static void main(String[] args){
User user = new User();
atomicReference.compareAndSet(u,user);
}
}
1.2 带版本号的原子引用类 AtomicStampedReference
创建一个版本号的初始版本号为1的User对象。
static User u = new User();
//原子引用
static AtomicStampedReference<User> atomicStampedReference = newAtomicStampedReference<User>(u,1);
2、使用AtomicStampedReference解决ABA问题
AtomicStampedReference中维护一个版本号,ABA问题可以通过每次更新操作递增一个版本号,来确认是否当前内存中的值是否是被修改过的。
public class UnsafeJH {
//原子引用
static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<Integer>(100,1);
public static void main(String[] args){
new Thread(()->{
int version = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName()+"\t第一次的版本号:"+version);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicStampedReference.compareAndSet(100,101,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
System.out.println(Thread.currentThread().getName()+"\t第2次的版本号:"+atomicStampedReference.getStamp());
atomicStampedReference.compareAndSet(101,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
System.out.println(Thread.currentThread().getName()+"\t第3次的版本号:"+atomicStampedReference.getStamp());
},"t1").start();
new Thread(()->{
int version = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName()+"\t第一次的版本号:"+version);
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean b = atomicStampedReference.compareAndSet(100,999,version,version+1);
System.out.println(Thread.currentThread().getName()+"修改成功否"+b+"\t当前值:"+atomicStampedReference.getReference());
},"t2").start();
}
运行结果:
t1 第一次的版本号:1
t2 第一次的版本号:1
t1 第2次的版本号:2
t1 第3次的版本号:3
t2修改成功否false 当前值:100
线程t1获取版本号之后,线程sleep1秒确保线程t2获取到的版本号与t1的一致。
线程t2获取到版本号之后,线程sleep3秒确保t1可以完成一组ABA操作。
最后发现t2的compareAndSet方法执行失败。因为当前的版本号已被t1改为了3,与预期的1不符,所以更新失败。
这样我们就可以使用带版本号的原子引用类型AtomicStampedReference类避免ABA的发生。