登录
首页 >  文章 >  java教程

ReentrantLock显式锁使用详解

时间:2026-04-24 22:12:56 276浏览 收藏

ReentrantLock 作为 Java 中功能更强大的显式锁,弥补了 synchronized 在尝试获取锁、限时等待、响应中断及灵活释放锁等方面的不足,但其使用门槛更高——必须严格遵循“lock() 后在 finally 块中配对 unlock()”的规范,否则极易引发死锁;它支持非阻塞抢锁(tryLock)、超时控制与公平策略,但公平锁会显著降低吞吐量,多数场景应坚持默认非公平模式;真正影响并发性能的往往不是锁类型的选择,而是锁粒度设计是否合理——选对工具只是起点,用好才是关键。

如何使用ReentrantLock显式锁替代同步块实现灵活加锁

为什么不能直接用 synchronized 就得换 ReentrantLock

当需要尝试获取锁(不阻塞)、限时等待锁、响应中断、或按需释放锁顺序时,synchronized 无能为力。ReentrantLock 提供了这些能力,但代价是必须显式 lock()unlock(),漏掉 unlock() 就会死锁。

典型场景包括:避免线程无限等待(如远程调用超时前放弃抢锁)、实现公平调度、或在 try 块中做多步操作后统一判断是否要释放锁。

lock() 必须配对 unlock(),且只能在 finally 块里写

这是最容易翻车的地方。写在 trycatch 里,一旦抛异常就跳过 unlock();写在 if 分支里,路径一多就漏。

  • 正确写法:始终把 unlock() 放在 finally 块中,且确保 lock() 成功后才进 finally
  • 错误写法:lock() 后直接跟 unlock()(没进 try-finally),或把 unlock() 放在 catch
  • 注意:不能在 lock() 失败时调用 unlock(),会抛 IllegalMonitorStateException
ReentrantLock lock = new ReentrantLock();
lock.lock(); // 成功才继续
try {
    // 临界区操作
} finally {
    lock.unlock(); // 这里必须执行,且只在 lock() 成功后才可能到达
}

tryLock() 实现非阻塞或限时抢锁

tryLock() 不会挂起线程,适合高并发下“抢不到就走”的策略;tryLock(long, TimeUnit) 则可用于设置最大等待时间,避免卡死。

  • 返回 false 表示当前未获得锁,不是异常,不要当成错误处理
  • 如果用了带超时的 tryLock(),超时后线程不会被中断,但可配合 Thread.interrupted() 做协作取消
  • 注意:超时单位必须用 TimeUnit,传整数毫秒会编译失败(方法重载不匹配)
if (lock.tryLock(100, TimeUnit.MILLISECONDS)) {
    try {
        // 拿到锁,执行业务
    } finally {
        lock.unlock();
    }
} else {
    // 抢锁失败,走降级逻辑(如返回缓存、抛限流异常)
}

公平锁与性能开销:别盲目设 true

new ReentrantLock(true) 开启公平模式后,线程按 FIFO 排队,避免饥饿,但吞吐量通常下降 20%~50%,因为每次都要检查队列头。

  • 默认非公平锁更高效,适用于大多数场景(JVM 的 synchronized 也是非公平的)
  • 只有明确遇到低优先级线程长期抢不到锁、且压测证实不公平导致问题时,才考虑公平锁
  • 公平锁无法解决“锁粒度不合理”或“临界区过长”这类根本问题,加了也白加

真正难的是锁范围控制——比如该锁对象 A 却锁了整个类,或该分段锁却用了单个 ReentrantLock。这种设计偏差,比选公平还是非公平影响大得多。

本篇关于《ReentrantLock显式锁使用详解》的介绍就到此结束啦,但是学无止境,想要了解学习更多关于文章的相关知识,请关注golang学习网公众号!

资料下载
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>