atomicmarkablereference通过原子封装引用与布尔标记,解决aba问题并支持逻辑删除;需用get(boolean[])获取快照后调用compareandset,确保引用和标记同时匹配才更新,适用于无锁栈、连接池等二态状态场景。

AtomicMarkableReference 是 Java 并发包(java.util.concurrent.atomic)中用于解决“带标记的原子引用更新”问题的工具类,特别适合需要**同时追踪对象引用是否被修改过、以及是否被逻辑删除(如标记为已失效)** 的场景,比如无锁栈、无锁队列中的节点回收、或 ABA 问题的增强型规避。
为什么需要 AtomicMarkableReference?
普通 AtomicReference 只能保证引用本身的原子更新,但无法区分“值没变但中间被改过又改回来”(即 ABA 问题)。而有些业务逻辑不仅关心引用是否变化,还关心“这个对象是否已被标记为无效”——比如一个连接池中的连接对象,即使它仍指向同一个实例,但若已被标记为“已关闭”,就不该再被复用。
AtomicMarkableReference 在内部用一个 Pair(不可变内部类)封装了 reference 和 mark(布尔标记),并通过 CAS 同时更新二者,从而实现“引用 + 标记”的原子性。
核心用法:创建、读取与原子更新
使用分三步:初始化、获取当前值和标记、执行带条件的原子更新。
-
创建实例:
AtomicMarkableReference<node> ref = new AtomicMarkableReference(new Node(), false);</node>
构造时传入初始引用和初始标记(如false表示“有效”)。 -
安全读取当前状态:
调用ref.get(boolean[] markHolder),其中markHolder是长度为 1 的数组,用于接收当前标记值:boolean[] mark = new boolean[1];<br>Node current = ref.get(mark);<br>boolean isMarked = mark[0]; // true 表示已标记(如已删除/失效)
-
原子更新(CAS):
compareAndSet(V expectedReference, V newReference, boolean expectedMark, boolean newMark)
只有当当前引用 ==expectedReference且当前标记 ==expectedMark时,才将引用设为newReference、标记设为newMark,并返回true。
典型应用场景:无锁栈中的“逻辑删除”
在实现 Treiber Stack(基于 CAS 的无锁栈)时,出栈操作可能遇到 ABA 问题:节点 A 出栈 → 被回收 → 新节点 A' 入栈(地址复用)→ 再次出栈误认为仍是原 A。引入标记可避免:
• 将栈顶节点的 next 字段用 AtomicMarkableReference<node></node> 存储;
• 出栈时先用 compareAndSet 尝试将当前 top 的 next 标记为 true(表示“该链接已失效”),再更新 top;
• 若标记成功,说明此链路未被其他线程抢先修改,可安全推进;否则重试。
这样,即使引用值出现 ABA,标记位的变化也能被检测到,从而打破“假相等”。
注意事项与常见陷阱
-
标记是布尔值,仅两种状态:适用于“有效/无效”“存在/已删除”等二元语义,不适用于多状态标记(此时考虑
AtomicStampedReference)。 -
mark 数组必须每次新建或重置:不能复用同一数组对象来多次接收不同时刻的标记,否则会读到旧值;推荐每次调用
get()时新建new boolean[1]。 - 不解决内存泄漏:标记只是逻辑删除,对象仍被强引用持有,需配合其他机制(如弱引用、Cleaner、或者显式清理)管理生命周期。
- 慎用于复杂对象图:只保障 reference + mark 的原子性,不保证其指向对象内部状态的线程安全。
掌握 AtomicMarkableReference 的关键在于理解它把“引用变更”和“业务状态变更”耦合进一次 CAS,让并发控制更贴近真实业务语义。用对了,就能在无锁结构中既避开 ABA,又表达出“这个东西虽然还在,但已经不能用了”的明确意图。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











