synchronized 必须与 wait()/notify() 配合使用,且在同一个锁对象上执行;条件判断必须用 while 循环防止虚假唤醒;状态修改与通知需原子化封装在 synchronized 块内。

Java 中 synchronized 必须和 wait()、notify() 配合使用,否则会直接抛出 IllegalMonitorStateException。它们不是独立指令,而是一套基于对象监视器(monitor)的协作协议——锁是前提,条件是核心,循环校验是安全底线。
必须在 synchronized 块中调用,且锁对象要一致
wait() 和 notify() 是 Object 类的 final 方法,调用前当前线程必须已持有目标对象的锁。JVM 会检查调用线程是否是该对象监视器的拥有者,不满足就报错。
- 正确写法:用同一个对象做
synchronized目标,也用它调用wait()或notify() - 错误写法:
synchronized(obj1) { obj2.wait(); }—— 锁对象和调用对象不一致 - 推荐实践:声明私有 final 锁对象,比如
private final Object lock = new Object();,避免this被外部误用干扰
条件判断必须用 while,不能用 if
线程可能被“虚假唤醒”(spurious wakeup)——没有收到 notify 就从 wait() 返回。只用 if 判断会导致线程跳过条件检查,直接执行后续逻辑,引发空指针、越界或状态错乱。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 错误示例:
if (queue.isEmpty()) { lock.wait(); }→ 醒来后直接queue.poll(),崩溃风险高 - 正确写法:
while (queue.isEmpty()) { lock.wait(); }→ 每次醒来都重新校验,不满足就继续等 - 共享条件变量(如
queue、ready)需由同一把锁保护,或用volatile保证可见性
notify 和 notifyAll 的选择要看等待语义
notify() 只唤醒一个等待线程,但无法指定唤醒谁;notifyAll() 唤醒全部,由 JVM 调度竞争锁。选错容易导致线程饥饿或逻辑遗漏。
- 适合
notify()的场景:单生产者-单消费者,或多个消费者等待同一类资源(如连接池取连接),唤醒任意一个即可 - 适合
notifyAll()的场景:多个线程等待不同条件(如有的等“数据就绪”,有的等“配置加载完成”),或存在广播需求(如全局关闭信号) - 注意:
notify()执行后不会立刻释放锁,只有退出整个synchronized块时锁才真正释放
状态修改与通知必须原子化
修改共享状态(如向队列加元素、设置标志位)和发出通知(notify())必须在同一个 synchronized 块内完成。拆开写会产生竞态窗口:其他线程可能在中间插入并破坏状态。
- 正确顺序:
synchronized(lock) { queue.add(item); lock.notify(); } - 危险写法:
synchronized(lock) { queue.add(item); } lock.notify();→ 中间可能被抢占 - 被唤醒线程抢到锁后,是从
wait()调用处继续执行,不是跳转到notify()后面的代码
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










