Tabla de contenido
Par de clases internas, par de campos, constructor
Método 3 getXXX, XXXcompareAndSet, set, AttempStamp
Campo UNSAFE, pairOffset, método casPair, objectFieldOffset
Introducción
package java.util.concurrent.atomic;
/**
* AtomicStampedReference维护一个对象引用和一个整数“stamp”,它可以自动更新。
*
* <p>注意:这个实现通过创建内部对象来维护stampedreference,这些对象表示“boxed”[reference, integer]对。
*
* @since 1.5
* @author Doug Lea
* @param <V> The type of object referred to by this reference
*/
public class AtomicStampedReference<V>
Par de clases internas, par de campos, constructor
// 你可能想问,反正静态内部类Pair是只给自己用的,何必将Pair定义成泛型类呢,完全可以如下定义。
// 一来这样就无法使用静态函数of了,构造器是私有的只能通过这个静态函数访问构造器(注释中有解释);
// 二来Pair对象并不需要作为成员内部类而存在,即Pair不需要持有外部类的引用。
private static class Pair<T> {
// reference成员保存你的引用型对象,stamp成员则保存你对象的版本号
// 为了让Pair对象不可变,让两个域都是final的。你没有办法修改Pair对象的成员,所以只能通过静态函数重新构造一个Pair对象。这一点很重要。
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);
}
}
// AtomicStampedReference有一个volatile的Pair<V>成员,这样,保证了pair成员的可见性。
private volatile Pair<V> pair;
/**
* 使用给定的初始值创建一个新的AtomicStampedReference。
*
* @param initialRef the initial reference
* @param initialStamp the initial stamp
*/
public AtomicStampedReference(V initialRef, int initialStamp) {
pair = Pair.of(initialRef, initialStamp);
}
Método 3 getXXX, XXXcompareAndSet, set, AttempStamp
/**
* 返回引用的当前值。
*
* getReference/getStamp都是通过volatile的成员pair获得的,所以具有可见性。
*
* @return the current value of the reference
*/
public V getReference() {
return pair.reference;
}
/**
* 返回戳记的当前值。
*
* @return the current value of the stamp
*/
public int getStamp() {
return pair.stamp;
}
/**
* 返回引用和戳记的当前值。典型用法是{@code int[1] holder; ref = v.get(holder); }.
*
* get函数是为了“一次性”获得存储对象和版本号。对象通过返回值,版本号通过int数组。
*
* 因为int作为参数只能值传递,而int数组是一个对象,可以引用传递,所以要使用int数组。由于只需要得到版本号,所以数组大小大于1就可以了。
*
* 当然,该函数是复合操作,不具有原子性。
*
* @param stampHolder an array of size of at least one. On return,
* {@code stampholder[0]} will hold the value of the stamp.
* @return the current value of the reference
*/
public V get(int[] stampHolder) {
Pair<V> pair = this.pair;
stampHolder[0] = pair.stamp;
return pair.reference;
}
/**
* 如果当前引用==期望的引用,并且当前戳记等于期望的戳记,则原子地将引用和戳记的值设置为给定的更新值。
*
* <p>可能会错误地失败,并且不提供排序保证,所以它很少是compareAndSet的合适选择。
*
* @param expectedReference the expected value of the reference
* @param newReference the new value for the reference
* @param expectedStamp the expected value of the stamp
* @param newStamp the new value for the stamp
* @return {@code true} if successful
*/
public boolean weakCompareAndSet(V expectedReference,
V newReference,
int expectedStamp,
int newStamp) {
return compareAndSet(expectedReference, newReference,
expectedStamp, newStamp);
}
/**
* 如果当前引用==期望的引用,并且当前戳记等于期望的戳记,则原子地将引用和戳记的值设置为给定的更新值。
*
* @param expectedReference the expected value of the reference
* @param newReference the new value for the reference
* @param expectedStamp the expected value of the stamp
* @param newStamp the new value for the stamp
* @return {@code true} if successful
*/
public boolean compareAndSet(V expectedReference,
V newReference,
int expectedStamp,
int newStamp) {
Pair<V> current = pair;
return
// 首先expectedReference == current.reference判断旧引用,和当前的是否一样;expectedStamp == current.stamp判断旧版本,和当前的是否一样。
expectedReference == current.reference &&
expectedStamp == current.stamp &&
// (newReference == current.reference && newStamp == current.stamp)判断 新引用和新版本号,是否和当前的相同,一般情况下,不会满足此判断的。
((newReference == current.reference &&
newStamp == current.stamp) ||
// 既然上一条不满足,则调用casPair,最终调用到Unsafe的compareAndSwapObject。 return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
// 注意,每次执行此函数时,都会重新构造Pair对象,这一点很关键。
// 这保证调用compareAndSwapObject函数时,参数expected和x肯定两个不同的对象,
// 就算最开始的expectedReference与newReference一样,且expectedStamp与newStamp一样,也会构造出一个新对象,当然这由于短路或不会执行到的。
// 完全有可能,从expectedStamp == current.stamp到casPair(current, Pair.of(newReference, newStamp))期间,线程被切换走了,
// 切换的时候pair成员变量已经被修改了,等切换回来,current这个局部变量就不和成员pair相等了,自然casPair会失败。
casPair(current, Pair.of(newReference, newStamp)));
}
/**
* 无条件地设置引用和戳记的值。
*
* 这是用来无条件设置的函数。因为不需要保持旧值是否相同。
* 如果 新引用和新版本号 和 当前的 一样,那么不需要更新pair成员。
*
* @param newReference the new value for the reference
* @param newStamp the new value for the stamp
*/
public void set(V newReference, int newStamp) {
Pair<V> current = pair;
if (newReference != current.reference || newStamp != current.stamp)
this.pair = Pair.of(newReference, newStamp);
}
/**
* 如果当前引用==期望的引用,则原子地将戳记的值设置为给定的更新值。
* 这个操作的任何给定调用可能会假的失败(返回false),但是当,当前值持有期望的值,
* 并且没有其他线程也试图设置值时,重复调用将最终成功。
*
* 此函数只更新版本号,不更新引用对象。
* 只需保证旧引用和当前的相同,casPair函数保证了 只有current局部变量与当前pair成员是同一个对象时,才更新。
*
* @param expectedReference the expected value of the reference
* @param newStamp the new value for the stamp
* @return {@code true} if successful
*/
public boolean attemptStamp(V expectedReference, int newStamp) {
Pair<V> current = pair;
return
expectedReference == current.reference &&
(newStamp == current.stamp ||
casPair(current, Pair.of(expectedReference, newStamp)));
}
Campo UNSAFE, pairOffset, método casPair, objectFieldOffset
// Unsafe mechanics
private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
// 获得pair域的偏移量。
private static final long pairOffset =
objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class);
private boolean casPair(Pair<V> cmp, Pair<V> val) {
// 最终调用Unsafe的compareAndSwapObject方法时,是不关心版本号的。compareAndSwapObject只关心是不是同一个对象。(但这样不会造成问题)
// 虽然根据上一条,感觉可能会有问题。但是由于静态内部类Pair每次都会新构造对象出来,
// 即使T reference, int stamp两个参数完全一样,也会构造出两个不同的对象,所以最终调用的compareAndSwapObject不关心版本号也没有关系。
// 综上,版本号是在Unsafe的CAS操作上进行的附加判断,准备的说,是先判断版本号,再通过CAS操作判断对象。
return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
}
static long objectFieldOffset(sun.misc.Unsafe UNSAFE,
String field, Class<?> klazz) {
try {
return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field));
} catch (NoSuchFieldException e) {
// 将异常转换为相应的错误
NoSuchFieldError error = new NoSuchFieldError(field);
error.initCause(e);
throw error;
}
}