这是AtomicReference的用例:
考虑充当数字范围的此类,并使用单独的AtmomicInteger变量维护上下限。
public class NumberRange {
// INVARIANT: lower <= upper
private final AtomicInteger lower = new AtomicInteger(0);
private final AtomicInteger upper = new AtomicInteger(0);
public void setLower(int i) {
// Warning -- unsafe check-then-act
if (i > upper.get())
throw new IllegalArgumentException(
"can't set lower to " + i + " > upper");
lower.set(i);
}
public void setUpper(int i) {
// Warning -- unsafe check-then-act
if (i < lower.get())
throw new IllegalArgumentException(
"can't set upper to " + i + " < lower");
upper.set(i);
}
public boolean isInRange(int i) {
return (i >= lower.get() && i <= upper.get());
}
}
setLower和setUpper都是先检查后执行序列,但是它们没有使用足够的锁定使其成为原子。如果该数字范围保持(0,10),并且一个线程调用setLower(5),而另一个线程调用setUpper(4),则在一些不幸的时间安排下,两个线程都将通过设置程序中的检查,并且将应用这两个修改。结果是该范围现在保持(5,4)无效状态。因此,尽管底层的AtomicIntegers是线程安全的,但复合类不是。可以通过使用AtomicReference来解决此问题,而不是使用单个AtomicIntegers作为上限和下限。
public class CasNumberRange {
//Immutable
private static class IntPair {
final int lower; // Invariant: lower <= upper
final int upper;
...
}
private final AtomicReference values =
new AtomicReference(new IntPair(0, 0));
public int getLower() { return values.get().lower; }
public int getUpper() { return values.get().upper; }
public void setLower(int i) {
while (true) {
IntPair oldv = values.get();
if (i > oldv.upper)
throw new IllegalArgumentException(
"Can't set lower to " + i + " > upper");
IntPair newv = new IntPair(i, oldv.upper);
if (values.compareAndSet(oldv, newv))
return;
}
}
// similarly for setUpper
}