这是我参与8月更文挑战的第8天,活动详情查看:8月更文挑战
AtomicReference
java.util.concurrent.atomic包下的AtomicInteger类可以对整数进行包装,使得类似于i++的操作可以变成原子操作,那么对于一般的对象类型要怎么实现原子操作呢,从Java 1.5开始,JDK提供了AtomicReference类来保证引用对象之间的原子性。
1 | java复制代码public class AtomicReferenceDemo { |
运行结果:
true stu=Student{name=’blue’, age=19}
但是和原来使用AtomicInteger一样,CAS的时候仍旧会产生ABA问题
产生ABA问题的代码
1 | java复制代码public class ABADemo { |
运行结果:res1,res2,res3都是true,这说明虽然线程t1修改了atomicReference中的值,但是由于修改后值与原来的一样,所以线程t2在判断的时候认为该值没有被修改过CAS操作成功。
AtomicStampedReference解决ABA问题
为了解决ABA问题,引入了AtomicStampedReference,AtomicStampedReference它内部不仅维护了对象值,还维护了一个时间戳(版本号)。当AtomicStampedReference对应的数值被修改时,除了更新数据本身外,还必须要更新时间戳。
AtomicStampedReference的构造器
在初始化的时候传入两个参数,初始化的引用值和版本号
1 | java复制代码public AtomicStampedReference(V initialRef, int initialStamp) { |
AtomicStampedReference的compareAndSet方法
1 | java复制代码public boolean compareAndSet(V expectedReference, |
- expectedReference : 期望值
- newReference : 想要更新成的新的值
- expectedStamp : 期望的版本号
- newStamp : CAS操作成功要更新成的版本号
- 然后每次操作的时候都会先比较版本号,版本号一致才能操作成功,每次操作成功后都将版本号增加+1(版本号只加不减)*
测试代码
1 | java复制代码import java.util.concurrent.TimeUnit; |
该代码模拟了线程1将atomicStampedReference值修改后又改回成原来的值的过程,观察版本号的变化以及最后线程2的CAS操作成功与否,运行结果如下:
1 | java复制代码t1线程拿到的初始版本号:1 |
总结
1.AtomicReference使得对象类型可以想整数类型一样保证包装,实现原子操作
2.AtomicStampedReference在原来AtomicReference基础上加入了stamp(版本号)这一属性,每次操作成功必定增加版本号,使得在CAS的时候不会出现ABA问题(因为数据被修改过版本号肯定不一样)。
本文转载自: 掘金