
本文详解synchronized内置监视器锁与java.util.concurrent.locks显式锁(如reentrantlock)的根本差异,阐明为何混用二者会导致illegalmonitorstateexception,并通过barrier实现对比展示两种机制的独立性、正确用法及最佳实践。
本文详解synchronized内置监视器锁与java.util.concurrent.locks显式锁(如reentrantlock)的根本差异,阐明为何混用二者会导致illegalmonitorstateexception,并通过barrier实现对比展示两种机制的独立性、正确用法及最佳实践。
在Java并发编程中,synchronized关键字与java.util.concurrent.locks包中的显式锁(如ReentrantLock)虽目标一致——实现线程同步与协作,但它们底层机制完全隔离、互不兼容。理解这一核心前提,是避免IllegalMonitorStateException或IllegalThreadStateException的关键。
? 本质差异:两套独立的同步体系
synchronized是JVM级内置机制:每个Java对象都关联一个隐式“监视器(monitor)”。当执行synchronized(obj)时,线程必须获取该对象的监视器锁;只有持有该锁,才能安全调用obj.wait()、obj.notify()或obj.notifyAll()。若未持锁即调用这些方法,JVM立即抛出IllegalMonitorStateException。java.util.concurrent.locks是API级显式机制:Lock(如ReentrantLock)不依赖对象监视器,而是通过内部状态(如AQS队列)自行管理所有权和等待线程。Condition是Lock的“条件变量工厂”,其await()/signal()方法仅校验当前线程是否持有该Lock实例,与任何synchronized块无关。若未通过lock.lock()获取锁就调用condition.await(),则抛出IllegalThreadStateException(注意:不是IllegalMonitorStateException)。
因此,synchronized(lock) 和 lock.lock() 完全不等价:前者锁定的是 lock 对象的监视器,后者锁定的是 ReentrantLock 实例自身的可重入锁状态。两者无任何映射关系——就像用两把不同钥匙试图打开同一扇门,钥匙根本不匹配。
❌ 错误示例解析:为何原代码报错?
原始代码中:
synchronized(lock) { // ✅ 获取 lock 对象的 monitor
incrementCondition.await(); // ❌ 但 incrementCondition 属于 ReentrantLock 实例,
// 它只认 lock.lock() 获得的锁,不认 synchronized 锁!
}
此处线程虽持有 lock 对象的监视器,却未持有 ReentrantLock 的逻辑锁,因此 incrementCondition.await() 检测失败,抛出异常(实际为 IllegalThreadStateException,原问题描述中的 IllegalMonitorStateException 可能为混淆或旧版JDK行为,但根本原因一致)。
✅ 正确实践:二选一,绝不混用
方案1:纯 synchronized + wait/notify(推荐简单场景)
public class SynchronizedBarrier {
private final Object lock = new Object();
private final int threshold;
private int count = 0;
private boolean released = false;
public SynchronizedBarrier(int threshold) {
this.threshold = threshold;
}
public void await() throws InterruptedException {
synchronized (lock) {
if (released) throw new IllegalStateException("Barrier already reset");
count++;
if (count == threshold) {
released = true;
lock.notifyAll(); // ✅ 在同一锁对象上调用
} else {
while (!released) {
lock.wait(); // ✅ 安全:已持有 lock 监视器
}
}
}
}
}
方案2:纯 Lock + Condition(推荐复杂控制需求)
public class LocksBarrier {
private final Lock lock = new ReentrantLock();
private final Condition barrierReached = lock.newCondition();
private final int threshold;
private int count = 0;
private boolean released = false;
public LocksBarrier(int threshold) {
this.threshold = threshold;
}
public void await() throws InterruptedException {
lock.lockInterruptibly(); // ✅ 显式获取锁
try {
if (released) throw new IllegalStateException("Barrier already reset");
count++;
if (count == threshold) {
released = true;
barrierReached.signalAll(); // ✅ Condition 与 lock 绑定
} else {
while (!released) {
barrierReached.await(); // ✅ 安全:已持有 lock
}
}
} finally {
lock.unlock(); // ✅ 必须在 finally 中释放,保证安全性
}
}
}
⚠️ 关键注意事项
-
禁止跨机制调用:
synchronized块内不可调用Condition.await();Lock.lock()后不可调用obj.wait()。 -
资源管理规范:显式
Lock必须配对unlock(),且强烈建议在try-finally中释放(如上例),避免死锁。 -
中断响应:
Lock.lockInterruptibly()和Condition.await()支持中断,而synchronized的wait()也支持,但需手动处理中断后状态(如递减计数器)。 -
性能与功能权衡:
synchronized更简洁、JVM优化成熟;Lock提供更细粒度控制(如公平锁、多条件变量、超时获取锁)。
✅ 总结
synchronized 与 java.util.concurrent.locks 是Java提供的两套正交并发工具。它们解决相同问题,但设计哲学与实现原理截然不同。混用二者不仅无效,而且必然失败。选择哪一种,应基于需求复杂度:简单同步首选 synchronized;需要高级特性(如多条件、可中断等待、超时)则选用 Lock + Condition,并严格遵循其API契约——永远只通过 lock.lock() 获取锁,只通过 condition.await() 等待,绝不越界。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











