Java中的CAS与ABA
🧩 一、CAS原理:图书馆占座的原子操作
1. 核心机制(Compare And Swap)
- 类比:图书馆唯一座位(内存地址V),你看到座位空着(期望值A),准备放书包占座(新值B)。若放包时座位仍空(
V==A
),占座成功;若已被占(V≠A
),则放弃。 - 本质:CPU硬件指令(如x86的
cmpxchg
)保证比较+交换的原子性,Java通过Unsafe
类封装。 - 代码示例:
AtomicInteger seat = new AtomicInteger(0); // 座位状态:0=空,1=占// 尝试占座(无锁) boolean success = seat.compareAndSet(0, 1); System.out.println("占座结果:" + success); // true
2. 工作流程
graph LRA[读取内存值V] --> B{是否等于期望值A?}B -->|是| C[更新为新值B]B -->|否| D[放弃操作]
⚠️ 二、ABA问题:座位被占两次的陷阱
1. 问题本质
- 场景:
线程1:座位空(A=0)→ 占座(B=1)
线程2:座位被占(1)→ 清空(A=0)
线程1:再次看到座位空(A=0) → 误判无人用过 → 成功占座(实际已被他人使用过)。 - 风险:数据状态被隐形篡改,如银行余额重复扣款。
2. 解决方案:加“版本号”
- 类比:给座位贴标签(如日期),每次使用更新标签。
- 代码示例:
AtomicStampedReference<Integer> seat = new AtomicStampedReference<>(0, 0); // 初始值=0,版本号=0// 第一次占座(版本号+1) seat.compareAndSet(0, 1, 0, 1); // 线程2尝试占座:虽然值=0,但版本号已变(预期版本0≠实际版本1) boolean success = seat.compareAndSet(0, 1, 0, 1); System.out.println("占座结果:" + success); // false
📱 三、Android中的应用场景
1. 全局计数器(无锁高性能)
- 场景:统计App启动次数、按钮点击量。
- 优势:避免
synchronized
锁导致主线程卡顿。 - 代码示例:
// MainActivity.kt val clickCount = AtomicInteger(0)fun onButtonClick() {val newCount = clickCount.incrementAndGet() // CAS自增runOnUiThread { tvCount.text = "点击次数: $newCount" } }
2. 状态标记(轻量同步)
- 场景:登录状态切换(如“未登录→登录中→已登录”)。
- 优势:比
volatile
更精确,比锁更高效。 - 代码示例:
enum class LoginState { IDLE, LOADING, SUCCESS }val loginState = AtomicReference(LoginState.IDLE)fun startLogin() {if (loginState.compareAndSet(LoginState.IDLE, LoginState.LOADING)) {// 发起网络请求} }
3. 资源池复用(避免ABA问题)
- 场景:
RecyclerView
的ViewHolder复用池中,防止对象被回收后重新分配导致数据错乱。 - 代码示例:
// ViewHolder复用池 AtomicStampedReference<ViewHolder> holderRef = new AtomicStampedReference<>(null, 0);fun getViewHolder(): ViewHolder {int[] stampHolder = new int[1];ViewHolder holder = holderRef.get(stampHolder);if (holder != null) {// 使用前检查版本号,防止已被回收的Holder被复用if (holderRef.compareAndSet(holder, null, stampHolder[0], stampHolder[0])) {return holder;}}return createNewViewHolder(); }
⚠️ 四、Android开发注意事项
-
避免主线程自旋:
CAS失败时会自旋重试,若在主线程长时间自旋会导致ANR。解决方案:限制重试次数或切子线程。fun safeUpdate() {var retry = 0while (retry++ < 3) { // 限制重试3次if (atomicRef.compareAndSet(...)) return}// 仍失败则用Handler/LiveData异步处理 }
-
优先选择原子类:
AtomicInteger
>synchronized
:计数器场景性能提升10倍+AtomicReference
>volatile
:多状态切换更安全
-
内存敏感设备优化:
AtomicFieldUpdater
比AtomicInteger
节省内存(不包装对象)。
💎 总结
- CAS是Android高并发基石,用硬件指令实现无锁线程安全,适合计数器、状态机等场景。
- ABA问题需用
AtomicStampedReference
(版本号)或AtomicMarkableReference
(布尔标记)解决。 - 选型口诀:
低竞争用原子类(AtomicInteger), 防ABA加版本号(AtomicStampedReference), 超高并发选分段(LongAdder)。
掌握CAS机制,能显著提升App并发性能与稳定性,尤其在列表滚动、高频点击等场景效果显著。