选锁还是无锁取决于操作是否复合、竞争是否激烈、读写比例如何:单变量原子更新优先CAS,低竞争用synchronized,高竞争需控制时选ReentrantLock,读多写少则用StampedLock或ReadWriteLock。

选锁还是无锁,关键看三个实际因素:操作是否复合、竞争是否激烈、读写比例如何。不是越新越好,也不是越轻量越优,而是哪一种能让临界区更短、线程等待更少、数据更稳。
先看操作本身:单变量原子更新优先用无锁
如果只是对一个 int、long、引用类型做“读-改-写”且逻辑简单(比如计数器自增、状态标志切换),AtomicInteger、AtomicReference 这类 CAS 工具足够安全又零阻塞。它们底层靠 CPU 的 compare-and-swap 指令实现,没有线程挂起/唤醒开销。
- 适用场景:统计埋点、限流令牌桶数量、开关状态位
- 注意限制:不能保护多步操作。比如“先查库存 > 再扣减 > 再更新订单”,这三步必须整体原子,CAS 无法覆盖,强行拆成多个原子操作会引入 ABA 问题或逻辑断裂
再看竞争程度:低竞争用 synchronized,高竞争且需控制时换 ReentrantLock
synchronized 在 JDK 6 之后已深度优化:无竞争时是偏向锁,轻度竞争升级为轻量级锁(基于 CAS 自旋),只有真正激烈争抢才膨胀为重量级锁。它语法简洁、自动释放、JVM 层面兜底,适合方法级或代码块级的简单同步。
- 推荐用法:getter/setter、工具类静态方法、配置加载一次后只读的场景
- ReentrantLock 更适合需要明确控制的情况:比如想在 200ms 内抢不到锁就放弃(
tryLock(200, TimeUnit.MILLISECONDS))、任务中途被中断要立刻响应(lockInterruptibly())、或者需要多个条件队列(newCondition())
最后看读写比例:读远多于写,别死守互斥锁
当读操作占比超过 80%,用普通锁会让大量读线程排队空等。这时应转向读写分离策略:
立即学习“Java免费学习笔记(深入)”;
- ReentrantReadWriteLock:读锁共享、写锁独占。适合配置中心、本地缓存这类“初始化后极少修改”的结构
-
StampedLock:支持乐观读(
tryOptimisticRead),成功则零开销;失败再降级为悲观读锁。性能通常优于读写锁,但要求读操作本身不依赖复杂校验逻辑 - 慎用:如果写操作虽少但延迟敏感(如实时风控规则更新),读写锁可能造成写饥饿;此时可考虑分段锁(StripedLocking)或 COW(CopyOnWriteArrayList)等变体
小结:一个快速决策路径
遇到并发访问共享数据,按顺序问自己:
- 是不是只改一个变量?→ 是 → 用 AtomicXXX
- 是不是读多写少(>5:1)?→ 是 → 看 StampedLock 或 ReadWriteLock
- 有没有超时、中断、轮询需求?→ 是 → 用 ReentrantLock
- 都不需要,只是简单同步?→ 用 synchronized
不复杂但容易忽略:无论哪种锁,都要确保临界区足够小——别在锁里调远程接口、解析大文件或执行慢 SQL。


















