[toc]
AtomicStampedReference源码阅读
CAS都是基于“值”来做比较的。但如果另外一个线程把变量的值从A改为B,再从B改回到A,那么尽管修改过两次,可是在当前线程做CAS操作的时候,却会因为值没变而认为数据没有被其他线程修改过,这就是所谓的ABA问题。要解决ABA 问题,不仅要比较“值”,还要比较“版本号”,而这正是AtomicStamped-Reference做的事情
1 | package java.util.concurrent.atomic; |
要解决Integer或者Long型变量的ABA问题,为什么只有AtomicStampedReference,而没有AtomicStampedInteger或者AtomictStampedLong呢?
因为这里要同时比较数据的“值”和“版本号”,而Integer型或者Long型的CAS没有办法同时比较两个变量,于是只能把值和版本号封装成一个对象,也就是这里面的Pair 内部类,然后通过对象引用的CAS来实现。
1 | // 要保存的值和时间戳对(封装成了一个对象) |
compareAndSet()函数
之前的CAS只有两个参数,这里的CAS有四个参数,后两个参数就是版本号的旧值和新值。
- 当expectedReference!=对象当前的reference时,说明该数据肯定被其他线程修改过;
- 当expectedReference==对象当前的reference时,再进一步比较expectedStamp是否等于对象当前的版本号,以此判断数据是否被其他线程修改过。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63public boolean compareAndSet(V expectedReference,
V newReference,
int expectedStamp,
int newStamp) {
Pair<V> current = pair;
// 短路操作,如果前面不满足,后面就不会执行了
return
expectedReference == current.reference &&//期望的引用与目前的引用是否相同
expectedStamp == current.stamp &&// 期望的时间戳(int)和目前的时间戳是否相同
((newReference == current.reference &&
newStamp == current.stamp) ||//如果新的reference和时间戳(int)和目前的一样也不用后序操作了
casPair(current, Pair.of(newReference, newStamp)));
}
/**
* Unconditionally sets the value of both the reference and stamp.
*
* @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;
// 如果引用不同或者时间戳(int值)不同,那么属于两个属于两个不同的,更新目前的时间戳(int)
if (newReference != current.reference || newStamp != current.stamp)
this.pair = Pair.of(newReference, newStamp);
}
// 如果当前引用 == 预期引用,则以原子方式将该标志的值设置为给定的更新值。
// 此操作的任何给定调用都可能会意外失败(返回 false),
// 但是在当前值保持预期值而且没有其他线程也在尝试设置该值时,重复调用将最终获得成功。
public boolean attemptStamp(V expectedReference, int newStamp) {
Pair<V> current = pair;
return
expectedReference == current.reference &&
(newStamp == current.stamp ||
casPair(current, Pair.of(expectedReference, newStamp)));
}
// Unsafe mechanics
private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
// 内存的偏移量
private static final long pairOffset =
objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class);
// 调用UNSAFE下的CAS方法
private boolean casPair(Pair<V> cmp, Pair<V> val) {
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) {
// Convert Exception to corresponding Error
NoSuchFieldError error = new NoSuchFieldError(field);
error.initCause(e);
throw error;
}
}
}
AtomicMarkableReference源码阅读
AtomicMarkableReference与AtomicStampedReference原理类似,只是Pair里面的版本号是boolean类型的,而不是整型的累加变量.
因为是boolean类型,只能有true、false 两个版本号,所以并不能完全避免ABA问题,只是降低了ABA发生的概率。
1 |
|