Java JUC 锁与原子类
java.util.concurrent 中的显式锁与原子类:相对 synchronized 的能力边界与适用场景。
#type / concept
#status / growing
#tech / dev
#resource / java
[!info] 关联笔记
Java JUC 锁与原子类
这个概念为什么出现
synchronized 简洁,但缺少超时获取、可中断获取、多条件队列等能力。java.util.concurrent.locks 与原子类提供更细控制与无锁算法基础(CAS)。
[!abstract] 一句话理解
ReentrantLock等提供显式加解锁与高级特性;Atomic*用 CAS 更新单变量;选它们是因为需求超出内置锁或单变量热点更新。
最小可运行示例
场景:抢券用原子计数;复杂临界区用 ReentrantLock 超时
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
public class CouponRace {
private final AtomicInteger remaining = new AtomicInteger(1);
private final ReentrantLock lock = new ReentrantLock();
boolean tryTakeAtomic() {
while (true) {
int cur = remaining.get();
if (cur <= 0) return false;
if (remaining.compareAndSet(cur, cur - 1)) return true;
}
}
boolean tryCriticalWork() throws InterruptedException {
if (lock.tryLock(50, TimeUnit.MILLISECONDS)) {
try {
return true; // 模拟临界区
} finally {
lock.unlock();
}
}
return false;
}
public static void main(String[] args) throws Exception {
CouponRace r = new CouponRace();
System.out.println(r.tryTakeAtomic());
System.out.println(r.tryTakeAtomic());
System.out.println(r.tryCriticalWork());
}
}
建议运行:
javac CouponRace.java && java CouponRace
期望输出:
true
false
true
结合场景再看三个关注点
- CAS 循环处理竞争。
unlock必须在 finally。- 超时锁避免死等。
核心概念与准确模型
1. ReentrantLock
- 可重入
- 公平/非公平
lockInterruptibly/tryLock- 多
Condition
2. 读写锁
ReadWriteLock:读多写少场景(先测量)
3. 原子类
AtomicInteger/AtomicReference/LongAdder等- 单变量更新;复杂不变量仍要锁或无锁结构
边界情况与反直觉行为
- 忘记 unlock → 永久阻塞。
- CAS ABA 问题(需要时用
AtomicStampedReference)。 - 公平锁吞吐可能更差。
常见误区
[!warning] 常见误区:所有地方替换为 ReentrantLock 无额外需求时 synchronized 更简单;可读性也是正确性的一部分。
工程实践
- 锁对象私有。
- 热点计数考虑 LongAdder。
- 与业务事务边界对齐,避免锁跨越远程调用。
- 死锁诊断用线程转储。
本节总结
- 显式锁=更多控制
- 原子类=单变量 CAS
- 简单场景不炫技
自测题
- 何时用 tryLock 超时?
- AtomicInteger 能否保护“扣库存并记日志两步不变量”?
参考答案
- 避免死等、实现降级快速失败。
- 不能单独保证多步骤不变量,需要更高层同步/事务设计。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| java.util.concurrent.locks | API | 锁 |
| Atomic variables tutorial | 教程 | 原子变量 |