关于AtomicStampedReference的compareAndSet方法及ABA问题的疑问
AtomicStampedReference的compareAndSet疑问与ABA问题解答
问题描述
我想使用AtomicStampedReference,但对其compareAndSet()方法存在一些疑问:我发现源码里用expectedReference == current.reference比较引用,这看起来总是会返回false,那为什么AtomicStampedReference还能解决ABA问题?
compareAndSet方法源码
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))); }
示例代码
class Counter { private final AtomicStampedReference<Integer> value; public Counter(int initialValue) { value = new AtomicStampedReference<>(initialValue, 1000); } public int getValue() { return value.getReference(); } public int increment() { int[] stampHolder = new int[1]; int current; int next; do { current = value.get(stampHolder); next = current + 1; } while (!value.compareAndSet(current, next, stampHolder[0], stampHolder[0] + 1)); return next; } }
核心解答
关于==比较引用的误区
你看到的expectedReference == current.reference并非总是false,核心原因在于你示例中使用的Integer类型:Java对**-128到127范围内的整数有缓存机制**,当你通过value.get(stampHolder)获取的current,是从缓存中取出的同一个Integer对象,和传入compareAndSet的current引用完全一致,此时==比较会成立。
如果超出这个缓存范围,==确实可能不成立,但这是合理的设计:如果当前引用和预期引用的对象本身就不同,根本不需要校验版本戳,直接判定CAS失败即可,这是一层快速过滤的逻辑。
为什么能解决ABA问题
ABA问题的本质是:对象从A被修改为B后又改回A,单纯的CAS会误以为对象未发生变化。而AtomicStampedReference通过同时校验引用值和版本戳解决这个问题:
- 每次修改引用时,必须同步更新版本戳;
- 即使引用从A→B→A,版本戳也会从
s1→s2→s3,此时预期版本戳s1和当前版本戳s3不匹配,CAS直接失败,避免了误判。
回到你的示例:每次调用increment()时,都会获取当前的引用值和版本戳,尝试将引用值+1的同时版本戳+1。如果期间有其他线程把值改回原数值,版本戳已经变化,当前线程的CAS会失败,必须重新获取最新值再尝试,保证了操作的正确性。
内容的提问来源于stack exchange,提问作者Jun Qiu
相关产品推荐
相关产品推荐

