ReentrantLock 的 lock() 和 unlock() 必须严格配对且 unlock() 必须置于 finally 块中,否则会导致锁永久占用;同一线程多次 lock() 需同等次数 unlock();锁应作为 final 实例字段初始化;tryLock() 仅在返回 true 时才可 unlock。

ReentrantLock 的 lock() 和 unlock() 必须严格配对,且 unlock() 必须放在 try-finally 块的 finally 中。这不是建议,而是硬性要求——漏掉或放错位置会导致锁永久占用,其他线程在 lock() 处无限等待,程序无声卡死。
基础写法:lock 在 try 外,unlock 在 finally 里
这是最安全、最标准的用法模式:
-
lock()调用必须在try块之前,确保无论临界区是否抛异常,锁都已被获取 -
unlock()只能写在finally块中,这样即使临界区return、throw或 JVM 意外中断,也能保证释放 - 绝不能把
unlock()放在catch里——异常发生时可能根本没拿到锁,调用unlock()会直接抛IllegalMonitorStateException
可重入场景下,加锁和解锁次数必须一致
ReentrantLock 允许同一线程多次 lock(),内部靠 hold count 计数。要真正释放锁,必须调用相同次数的 unlock():
- 比如线程 A 调用了 3 次
lock(),那么必须调用 3 次unlock(),第 3 次之后锁才变为可用状态 - 每次
lock()都应有对应的unlock(),推荐在同一个方法层级成对出现,避免跨方法或条件分支遗漏 - 可在调试时用
lock.getHoldCount()查看当前线程持有锁的次数,辅助验证逻辑
别在错误时机创建或初始化锁
锁对象本身不是线程安全的初始化目标:
立即学习“Java免费学习笔记(深入)”;
- 不要在静态块、构造方法或单例初始化过程中,刚 new 出
ReentrantLock就立刻调用lock()—— 此时可能有多个线程并发执行初始化逻辑,极易引发 NPE 或死锁 - 锁实例应作为实例字段(
private final ReentrantLock lock = new ReentrantLock();)或明确的延迟初始化对象,确保其生命周期清晰可控 - 避免将一个锁实例混用于多组不相关的共享资源,否则会人为扩大临界区,降低并发度
用 tryLock() 时,只在成功获取后才 unlock
tryLock() 是唯一能“试探性”加锁的方法,它不阻塞,返回 true/false 表示结果:
- 只有
if (lock.tryLock()) { ... }分支内才需要(也必须)配对unlock(),且同样要放在finally中 - 如果
tryLock()返回false,说明此刻锁被占,此时绝对不能调用unlock(),否则报错 - 带超时的
tryLock(100, TimeUnit.MILLISECONDS)同理:超时返回false也不可 unlock;仅当返回true才进入临界区并最终释放


















