在我们使用CAS操作的时候会有一个问题那就是CAS过程中,预期值可能被更新了多次,最终又更新会预期值,这样判断cas操作则是成功的因为本次cas操作符合条件。
AtomicStampedReference总的来说就是解决一个线程将A改成C,也就是说当前值是A。
但是会出现A被改成B后又改回A,那么A改成C还是成功的。有些时候是不允许这种情况。因此有了AtomicStampedReference类,它在CAS的基础上加了一个时间戳的概念,实际上就是在原先需要预期值和新值,两个参数,现在需要另外加两个参数也就是预期版本号、更新后的版本号。如果预期值或预期版本号其中一个不符合就会更新失败。
常用的方法:
- 更新操作,需要传入4个参数:
weakCompareAndSet()
或者compareAndSet()
两者效果一致 - 获取当前值,不需要参数
getReference()
- 获取当前版本号,不需要参数
getStamp()
- 更新时间戳
attemptStamp(预期版本号 , 新版本号)
-
无条件更新
当前值和当前版本号set( 新值, 新版本号 )
- 返回当前值和当前版本号
get(int[] stampHolder)
当前值通过返回值得到,当前版本号则在stampHolder[0]
中
另外一个相识的类 AtomicMarkableReference
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
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;
}
// 得到一个Pair实例
static <T> Pair<T> of(T reference, int stamp) {
return new Pair<T>(reference, stamp);
}
}
private volatile Pair<V> pair;
private static final VarHandle PAIR;
static {
try {
MethodHandles.Lookup l = MethodHandles.lookup();
PAIR = l.findVarHandle(AtomicStampedReference.class, "pair", Pair.class);
} catch (ReflectiveOperationException e) {
throw new ExceptionInInitializerError(e);
}
}
private boolean casPair(Pair<V> cmp, Pair<V> val) {
return PAIR.compareAndSet(this, cmp, val);
}
/**
* 创建一个有初始值和初始版本号的AtomicStampedReference实例
*/
public AtomicStampedReference(V initialRef, int initialStamp) {
pair = Pair.of(initialRef, initialStamp);
}
/**
* 返回当前值
*/
public V getReference() {
return pair.reference;
}
/**
* 返回当前版本号
*/
public int getStamp() {
return pair.stamp;
}
/**
* 返回当前值并将数组中的时间戳更新为当前的时间戳
*/
public V get(int[] stampHolder) {
Pair<V> pair = this.pair;
stampHolder[0] = pair.stamp;
return pair.reference;
}
/**
* cas操作,多个期望的时间戳和更新后的时间戳两个参数
*/
public boolean weakCompareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp) {
return compareAndSet(expectedReference, newReference, expectedStamp, newStamp);
}
/**
* 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)));
}
/**
* 无条件的更新当前值和当前时间戳
*/
public void set(V newReference, int newStamp) {
Pair<V> current = pair;
if (newReference != current.reference || newStamp != current.stamp)
this.pair = Pair.of(newReference, newStamp);
}
/**
* 如果是预期值则更新时间戳
*/
public boolean attemptStamp(V expectedReference, int newStamp) {
Pair<V> current = pair;
return
expectedReference == current.reference &&
(newStamp == current.stamp ||
casPair(current, Pair.of(expectedReference, newStamp)));
}
}