跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

Java中的ReentrantLock怎么用_可重入锁的lock与unlock操作及异常处理规范

ReentrantLock 必须配对调用 lock() 和 unlock(),且 unlock() 必须放在 finally 块中;推荐使用 tryLock() 避免死锁、lockInterruptibly() 支持中断响应;可重入性需严格匹配加锁解锁次数,公平模式会降低吞吐量。 ReentrantLock 必须配对调用
lock()
和
unlock()
,否则会永久阻塞后续线程 Java 的
ReentrantLock
不像
synchronized
那样自动释放锁,一旦忘记在
finally
块里调用
unlock()
,锁就一直被占着,其他线程卡死在
lock()
上,且不会抛异常提示你——它只是安静地等下去。 常见错误现象:
Thread A
拿到锁后因未捕获的异常提前退出,没走到
unlock()
;
Thread B
在
lock()
处无限等待,CPU 占用低但业务完全卡住。 必须把
lock()
放在
try
前,
unlock()
放在
finally
块中(哪怕 try 里 return 或 throw) 不能把
unlock()
写在
catch
里——异常时可能根本没拿到锁,强行 unlock 会抛
IllegalMonitorStateException
不要在构造函数、静态初始化块等非线程安全上下文中直接 new
ReentrantLock()
后立刻
lock()
,容易引发死锁或 NPE
ReentrantLock lock = new ReentrantLock(); lock.lock(); // 必须在 try 外 try { // 临界区操作 } finally { lock.unlock(); // 唯一安全位置 }
用
tryLock()
避免死锁,但要注意超时返回 false 的语义
tryLock()
是唯一能“不阻塞地尝试获取锁”的方式,适合做锁降级、资源探测或避免线程长时间挂起。但它不是“失败重试”的替代品——返回
false
只代表此刻拿不到锁,不代表锁坏了或出错了。 使用场景:两个线程需按顺序获取两把锁(A 和 B),若都用
lock()
容易形成 AB-BA 死锁;改用
tryLock()
+ 回退机制可破局。 立即学习 “ Java免费学习笔记(深入) ”;
tryLock()
立即返回
true/false
,无参数版本不等待;带
time, unit
的版本会在超时后返回
false
,不是抛异常 如果返回
false
,别直接重试——高并发下反复自旋浪费 CPU;应加随机延迟或改走备选逻辑(如降级为读缓存) 调用
tryLock()
成功后,仍必须在
finally
中
unlock()
,和
lock()
一样严格
if (lock.tryLock(100, TimeUnit.MILLISECONDS)) { try { // 操作 } finally { lock.unlock(); } } else { // 处理“拿不到锁”的情况,比如返回默认值或记录日志 }
中断响应:只有
lockInterruptibly()
支持线程中断,
lock()
会忽略
interrupt()
当线程在
lock()
上阻塞时,调用它的
interrupt()
不会打断等待,只会把中断状态设为
true
,等它终于拿到锁后才抛
InterruptedException
——这已经晚了。真正想实现“可中断等待”,必须用
lockInterruptibly()
。 Eclipse导入Android或其他的JAVA项目的正确方法 WORD版 本文档主要讲述的是Eclipse导入Android或其他的JAVA项目的正确方法;希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看 下载 性能影响:三者性能基本一致,但
lockInterruptibly()
多一次中断状态检查,开销可忽略;兼容性上,所有 JDK 6+ 都支持。
lock()
:无视中断,适合确定能快速获取锁的场景(如锁粒度极小)
lockInterruptibly()
:阻塞中响应中断,适合有明确超时或协作终止需求的服务(如定时任务 shutdown)
tryLock()
:不阻塞,自然也不涉及中断问题
try { lock.lockInterruptibly(); // 这里会响应 interrupt() try { // 临界区 } finally { lock.unlock(); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); // 恢复中断状态 // 处理中断,比如清理资源、退出循环 }
可重入性不是免费的——递归调用要小心锁计数与公平性配置
ReentrantLock
允许同一线程多次
lock()
,每次对应一次
unlock()
,内部靠计数器维护。但这个特性常被误用:有人以为“多 lock 几次更保险”,结果
unlock()
次数不够,锁永远不释放;或开了公平模式(
new ReentrantLock(true)
)却在递归中频繁进出,导致吞吐量断崖下降。 参数差异:
ReentrantLock()
默认非公平,吞吐高;
ReentrantLock(true)
公平,FIFO 排队,但上下文切换多、性能低 20%+(JMH 测试常见)。 重入次数必须和
unlock()
次数严格相等,建议用计数器或日志辅助验证(尤其在复杂条件分支中) 公平锁只保证“等待时间最长的线程优先”,不保证“先调用
lock()
的先拿到”,且无法解决锁竞争本身,只是换种排队方式 不要在持有锁期间调用外部不可控代码(如回调、SPI 实现),它可能再次进入同一把锁,造成隐蔽的重入逻辑 复杂点在于:重入行为和公平策略是正交的,但组合使用时,调试难度陡增。比如一个公平锁 + 多层递归调用 + 异步回调,出问题时很难分清是排队逻辑错,还是计数没对齐。

相关文章