这三个都是自 JDK1.5 开始加入到 java.util.concurrent.atomic 下面的。他们都可以在 lock-free 的情况下以原子的方式更新对象引用。
一、AtomicReference
以原子方式更新对象引用。
static class User {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public User(int age) {
this.age = age;
}
}
public static void main(String[] args) {
User user1 = new User(10);
User user2 = new User(20);
AtomicReference<User> atomicReference = new AtomicReference<>(user1);
System.out.println(atomicReference.get().getAge());
atomicReference.compareAndSet(user1, user2);
System.out.println(atomicReference.get().getAge());
}
知识兔二、AtomicStampedReference
解决了 AtomicReference 中 CAS 操作存在的 ABA 问题。
public static void main(String[] args) {
User user1 = new User(10);
User user2 = new User(20);
AtomicStampedReference<User> stampedReference = new AtomicStampedReference<>(user1, 1);
int[] stamp = new int[1];
// 获取引用对象和对应的版本号
System.out.println(stampedReference.get(stamp).getAge());
int oldStamp = stamp[0];
// 预期引用,新引用,预期版本号,新版本号
stampedReference.compareAndSet(user1, user2, oldStamp, 2);
System.out.println(stampedReference.get(stamp).getAge());
}
知识兔內部定义了一个 Pair 对象,相当于给引用加了一个版本号
public class AtomicStampedReference<V> {
private static class Pair<T> {
final T reference;
final int stamp;
private Pair(T reference, int stamp) {
this.reference = reference;
this.stamp = stamp;
}
static <T> Pair<T> of(T reference, int stamp) {
return new Pair<T>(reference, stamp);
}
}
private volatile Pair<V> pair;
public AtomicStampedReference(V initialRef, int initialStamp) {
pair = Pair.of(initialRef, initialStamp);
}
知识兔替换时的逻辑,当引用和版本号都相同时才使用 CAS 替换
public boolean compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp) {
Pair<V> current = pair;
return expectedReference == current.reference &&
expectedStamp == current.stamp &&
((newReference == current.reference &&
newStamp == current.stamp) ||
casPair(current, Pair.of(newReference, newStamp)));
}
private boolean casPair(Pair<V> cmp, Pair<V> val) {
return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
}
知识兔三、AtomicMarkableReference
相对于 AtomicStampedReference,有时候,我们并不关心引用变量更改了几次,只是单纯的关心是否更改过
public static void main(String[] args) {
User user1 = new User(10);
User user2 = new User(20);
AtomicMarkableReference<User> stampedReference = new AtomicMarkableReference<>(user1, false);
boolean[] stamp = new boolean[1];
// 获取引用对象和对应的状态
System.out.println(stampedReference.get(stamp).getAge());
boolean oldStamp = stamp[0];
// 预期引用,新引用,预期状态,新状态
stampedReference.compareAndSet(user1, user2, oldStamp, false);
System.out.println(stampedReference.get(stamp).getAge());
}
知识兔内部和 AtomicStampedReference 一样
public class AtomicMarkableReference<V> {
private static class Pair<T> {
final T reference;
final boolean mark;
private Pair(T reference, boolean mark) {
this.reference = reference;
this.mark = mark;
}
static <T> Pair<T> of(T reference, boolean mark) {
return new Pair<T>(reference, mark);
}
}
private volatile Pair<V> pair;
public AtomicMarkableReference(V initialRef, boolean initialMark) {
pair = Pair.of(initialRef, initialMark);
}
public boolean compareAndSet(V expectedReference, V newReference, boolean expectedMark, boolean newMark) {
Pair<V> current = pair;
return
expectedReference == current.reference &&
expectedMark == current.mark &&
((newReference == current.reference &&
newMark == current.mark) ||
casPair(current, Pair.of(newReference, newMark)));
}
private boolean casPair(Pair<V> cmp, Pair<V> val) {
return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
}
知识兔