synchronized不能中断等待线程,因其底层monitor不响应interrupt信号;而ReentrantLock的lockInterruptibly()可响应中断并抛出InterruptedException。

为什么 synchronized 不能中断等待线程
synchronized 是 JVM 层的内置锁,进入阻塞状态后线程会挂起,且无法被 Thread.interrupt() 中断。这是由底层 monitor 实现决定的——它不响应中断信号,只会一直等锁释放。
而 Lock 接口(如 ReentrantLock)提供了 lockInterruptibly() 方法,调用时若线程被中断,会抛出 InterruptedException 并立即退出等待队列。
- 使用
synchronized时,即使调用thread.interrupt(),线程仍卡在monitorenter指令上,无法响应 -
ReentrantLock.lockInterruptibly()在获取锁前会检查中断状态,适合需要超时或可取消的场景(如任务调度、RPC 调用) - 注意:
lockInterruptibly()不是“自动中断”,必须配合外部中断逻辑(比如另一个线程调用interrupt())
tryLock() 和 wait/notify 不能混用
synchronized 块内可以安全使用 Object.wait() / notify(),因为它们绑定的是同一个 monitor 对象;但 Lock 不提供原生的等待/唤醒机制,必须搭配 Condition 使用。
直接在 ReentrantLock 保护的代码里调用 wait() 会抛 IllegalMonitorStateException,因为此时线程并未持有该对象的 monitor 锁。
立即学习“Java免费学习笔记(深入)”;
-
synchronized(obj) { obj.wait(); }合法,obj就是 monitor -
lock.lock(); obj.wait();非法,obj和锁无关 - 正确方式是:
Condition condition = lock.newCondition(); condition.await(); condition.signal(); - 一个
Lock可创建多个Condition,实现更精细的等待队列(比如读写锁中分别管理读等待和写等待)
公平性与性能差异在哪体现
synchronized 始终是非公平的,且无法配置;ReentrantLock 默认也是非公平,但构造时传 true 可启用公平模式:new ReentrantLock(true)。
公平锁会按线程入队顺序分配锁,避免饥饿,但吞吐量明显下降——每次加锁都要遍历同步队列确认头节点,还可能引发频繁的线程挂起/唤醒切换。
- 高并发争抢下,非公平锁平均性能比公平锁高 2–3 倍(JDK8 测试数据)
-
synchronized经过 JIT 优化后,在无竞争或轻度竞争时几乎无额外开销;而ReentrantLock即使未争抢,也有对象创建和方法调用成本 - 公平锁只应在明确存在饥饿风险(如定时任务线程长期拿不到锁)时启用,不是“更安全”的默认选项
锁升级失败会导致 synchronized 退化为重量级锁
JVM 对 synchronized 做了多层优化:偏向锁 → 轻量级锁 → 重量级锁。一旦发生锁竞争(比如两个线程同时进入同一把锁),偏向锁会被撤销,后续再进入就会直接走轻量级锁路径;若自旋失败(默认 10 次),就膨胀为重量级锁,线程进入操作系统 Mutex 等待。
这个过程不可逆,且撤销偏向锁需全局安全点(stop-the-world),可能造成明显停顿。
-
ReentrantLock没有这种“锁升级”概念,始终基于 AQS 队列,行为稳定可预期 - 可通过 JVM 参数关闭偏向锁(
-XX:-UseBiasedLocking)来规避撤销开销,尤其在容器环境或短生命周期应用中 - 注意:JDK15+ 默认禁用偏向锁,JDK17 彻底移除,所以新项目基本不用考虑这部分复杂性
public class LockVsSyncExample {
private final Object syncObj = new Object();
private final ReentrantLock lock = new ReentrantLock();
public void syncMethod() {
synchronized (syncObj) {
// 正确:wait 必须在 synchronized 块中
try {
syncObj.wait(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
public void lockMethod() {
lock.lock();
try {
// 错误:不能在这里调用 wait()
// syncObj.wait(); // 抛 IllegalMonitorStateException
// 正确:用 Condition
Condition condition = lock.newCondition();
condition.await(100, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}
}
锁的语义一致性比语法糖更重要。很多人以为 ReentrantLock 就是 synchronized 的升级版,其实它们适用边界很不同:前者适合需要中断、超时、多条件等待的显式控制场景;后者在大多数简单同步需求中更轻量、更不易出错。真正容易被忽略的是——synchronized 的 monitor 本质决定了它和 JVM 内存模型、GC 安全点、逃逸分析深度耦合,而这些细节在调试死锁或性能抖动时才突然变得关键。










