主要就是CAS与锁
一、CAS(无锁机制)
1. 核心思想
通过 硬件指令(如 CPU 的 cmpxchg) 实现原子操作,无需线程阻塞,直接在用户态完成竞争。
2. 特点
-
优点:
- 无锁,高并发下吞吐量高。
- 避免线程上下文切换和阻塞开销。
-
缺点:
- ABA 问题:值从 A → B → A,CAS 无法感知中间变化(可通过
AtomicStampedReference解决)。 - 自旋开销:高竞争时 CAS 失败率高,CPU 空转。
- ABA 问题:值从 A → B → A,CAS 无法感知中间变化(可通过
3. 应用场景
- 简单原子操作(如计数器增减)。
- 低竞争环境(如线程交替修改共享变量)。
- Java 中的
AtomicXXX类、LongAdder等均基于 CAS。
二、锁(Locking)
1. 核心思想
通过 互斥(Mutex) 强制代码块串行执行,保证原子性,依赖操作系统或 JVM 的线程调度。
2. 特点
-
优点:
- 严格保证原子性,适合复杂操作(如先读后写)。
- 解决 CAS 无法处理的复合操作问题。
-
缺点:
- 线程阻塞和唤醒涉及内核态切换,性能开销大。
- 可能导致死锁、优先级反转等问题。
3. 应用场景
- 高竞争环境(如多线程频繁争抢同一资源)。
- 复合操作(如银行转账:先扣减 A,再增加 B)。
- Java 中的
synchronized、ReentrantLock均基于锁。
三、CAS 与锁的对比
| 维度 | CAS | 锁 |
|---|---|---|
| 实现方式 | 硬件指令(用户态) | 操作系统互斥量(内核态) |
| 竞争处理 | 自旋重试 | 线程阻塞等待 |
| 适用场景 | 低竞争、简单操作 | 高竞争、复杂操作 |
| 性能开销 | 低(无阻塞) | 高(上下文切换) |
| 典型问题 | ABA 问题 | 死锁、饥饿 |
四、其他辅助机制
虽然 CAS 和锁是核心,但 Java 还提供了一些高级工具来优化原子操作:
-
volatile关键字:- 保证可见性和有序性,但不保证原子性。
- 通常与 CAS 结合使用(如
AtomicInteger内部依赖volatile变量)。
-
LongAdder分段锁:- 通过分散竞争(类似 ConcurrentHashMap 的分段锁)减少 CAS 冲突。
- 高并发下性能优于
AtomicLong。
-
VarHandle(Java 9+) :- 提供更灵活的内存操作 API(如原子字段更新),替代
Unsafe类。
- 提供更灵活的内存操作 API(如原子字段更新),替代
五、如何选择?
- 优先 CAS: 简单操作(如计数器)且竞争不激烈时,用
AtomicXXX类。 - 慎用锁: 复杂操作或高竞争时,用
synchronized或ReentrantLock。 - 特定优化: 高并发统计场景用
LongAdder,复合操作考虑锁或StampedLock。
总结
Java 原子操作的底层本质确实是 CAS 和 锁,但两者并非对立关系:
- CAS 是乐观锁(假设无冲突,失败重试)。
- 锁是悲观锁(假设有冲突,直接互斥)。
实际开发中需根据场景权衡,甚至混合使用(如 ConcurrentHashMap 分段锁 + CAS)。