ReentrantLock 保护计数器应缩小锁粒度,仅锁定写操作(如 value++),读操作用 volatile 即可;优先选 AtomicInteger,仅在需中断、超时或复杂逻辑时才用 ReentrantLock。

用 ReentrantLock 保护计数器,关键不是“锁住整个方法”,而是减少锁粒度、避免无谓等待,并配合 volatile 或原子类做轻量协同 —— 单纯套个 lock() / unlock() 反而可能比 synchronized 还慢。
只锁真正需要互斥的写操作
计数器的读操作(如 get())通常不需要加锁,除非你要求严格实时一致性。写操作(increment、decrement)才需互斥。把 lock/unlock 严格限定在修改共享变量的几行代码内,而不是包裹整个方法体。
- 错误做法:在 increment() 方法开头 lock,结尾 unlock,中间还调用了日志、校验等无关逻辑
- 正确做法:仅对
value++或value += delta这一行(或几行)加锁 - 示例:
lock.lock(); try { value++; } finally { lock.unlock(); }
优先使用 tryLock 避免线程阻塞
如果业务允许“尝试更新失败就跳过或重试”,用 tryLock() 可显著降低线程调度开销,尤其在高竞争场景下比无条件 lock() 更友好。
- 设置超时(如
tryLock(1, TimeUnit.MICROSECONDS))能防止长等待 - 失败时可选择:直接返回、退避后重试、降级为 CAS 尝试(配合 volatile value)
- 注意:不要在未获取锁时执行 unlock(),容易抛 IllegalMonitorStateException
读多写少时,用 volatile + 锁分离读写
若读操作远多于写(比如监控计数器),可将 value 声明为 volatile,并在写操作中加锁更新;读操作直接读 volatile 变量 —— 这样读不阻塞、写仍安全。
立即学习“Java免费学习笔记(深入)”;
- volatile 保证可见性和有序性,满足“读到最新写入值”的基本需求
- 写操作仍需锁,因为
value++是读-改-写三步,volatile 无法保证原子性 - 比 synchronized 更灵活(支持中断、超时、多条件队列),但编码责任更重
考虑是否真需要 ReentrantLock
简单计数器优先用 AtomicInteger:底层是 CAS,无锁、无上下文切换,在绝大多数场景下性能更好、代码更简。
- ReentrantLock 的优势在于可中断、可超时、可绑定多个 Condition —— 如果你没用到这些特性,大概率是过度设计
- 只有当计数逻辑复杂(如需校验、联动更新其他状态、嵌套锁)时,ReentrantLock 才体现价值
- 别为了“看起来更高级”而放弃 Atomic* 类 —— 它们经过充分优化,且线程安全语义更清晰
不复杂但容易忽略:锁的公平性默认是 false,设为 true 会明显降低吞吐量,除非你明确需要按请求顺序调度。



















