atomicmarkablereference 是用于原子更新“带布尔标记的引用”的类,解决引用不变但逻辑状态(如删除、锁定)需同步变更的竞态问题;它通过内部版本戳防aba,仅暴露boolean标记,适用于双态场景,使用时必须用get(boolean[])获取最新快照再cas。

AtomicMarkableReference 是什么,什么时候非用不可
它解决的是“带标记的引用”需要原子更新的场景,比如一个对象本身没变,但它的逻辑状态(如是否已删除、是否被锁定)变了,你既要读取对象,又要同时读取/更新这个标记,且整个操作不能被其他线程打断。普通 AtomicReference 只能保证引用更新原子,没法附带一个布尔标记;而用两个独立的原子变量(AtomicReference + AtomicBoolean)又无法保证二者更新的整体原子性——这就是 AtomicMarkableReference 存在的唯一理由。
- 标记只能是
boolean,不是任意类型 - 内部用“版本戳+标记”方式实现 ABA 防御,但不暴露版本号,只暴露标记
- 适用于轻量级双态标记:例如缓存项是否已失效、节点是否已被逻辑删除、连接是否处于“正在关闭”状态
compareAndSet 怎么写才不会白忙活
compareAndSet 要求你提供「旧引用值」「旧标记值」「新引用值」「新标记值」四个参数,任何一项不匹配都会失败。常见错误是只关心引用是否相同,却忽略标记当前值。
- 必须先调用
get或get(boolean[])拿到当前的引用和标记快照,再基于这个快照构造compareAndSet调用 - 错误示范:
ref.compareAndSet(oldObj, newObj, true, true)—— 这里oldObj可能早已被别的线程替换成newObj,但标记已是false,这次调用必然失败 - 正确做法是用
boolean[] holder = new boolean[1]接收标记,再传给compareAndSet:boolean[] mark = new boolean[1]; Node current = ref.get(mark); while (!ref.compareAndSet(current, current, mark[0], !mark[0])) { current = ref.get(mark); }
为什么 get(boolean[]) 比单独 get() 多一层必要
get() 只返回引用,丢掉了标记;get(boolean[]) 把标记写入你传入的数组第一个元素,这是唯一能一次性拿到引用+标记快照的方式。漏掉这一步,就等于在竞态条件下凭空猜标记值。
- 数组长度必须 ≥ 1,否则抛
NullPointerException - 数组只是个容器,不参与原子性保障,内容由方法内部写入
- 如果你只需要标记不需要引用,仍得调用
get(new boolean[1]),别试图绕开
和 AtomicStampedReference 选哪个
两者都防 ABA,但设计目标不同:AtomicStampedReference 带整数版本号,适合需要多状态或递增版本的场景(如重试计数、乐观锁版本);AtomicMarkableReference 只带一个布尔标记,更轻量,语义更明确。
立即学习“Java免费学习笔记(深入)”;
- 如果标记只有“是/否”两种含义,选
AtomicMarkableReference:代码更直白,内存占用略小(int vs boolean) - 如果将来可能扩展成“未开始/进行中/已失败/已重试2次”,立刻换
AtomicStampedReference - 别混用:拿
AtomicMarkableReference的引用去塞进AtomicStampedReference,编译过不了,运行也无意义
标记和引用是一体两面,拆开读就是埋雷;CAS 不是重试次数够多就能赢,而是每次重试都得基于最新快照——这点容易被忽略,尤其在嵌套条件判断里。










