为什么 Java 中 wait() 和 notify() 必须在 synchronized 代码块中使用
                           
天天向上
发布: 2025-07-13 11:41:39

原创
591 人浏览过

一、核心结论(官方定义)

Java 中的 Object.wait()Object.notify()Object.notifyAll() 必须在同步上下文(synchronized 块或方法)中被调用,否则会抛出 java.lang.IllegalMonitorStateException

这是由 Java 虚拟机的对象监视器(Monitor)机制决定的。

📚 官方文档说明(Java SE 8+):

原文:

This method should only be called by a thread that is the owner of this object’s monitor.
A thread becomes the owner of the object’s monitor in one of three ways:

  • By executing a synchronized instance method of that object.
  • By executing the body of a synchronized statement that synchronizes on the object.
  • For class objects, by executing a synchronized static method of that class.

二、详细技术分析

1. 背景:Java 中的对象监视器 Monitor

每个 Java 对象都可以用作一把锁(Lock),也称为“监视器锁”。

  • 当一个线程进入 synchronized(obj) 时,它会尝试获取 obj 的 Monitor
  • Monitor 是 JVM 的一种同步机制,是 wait() / notify() 等行为的基础。

2. wait()notify() 的内部原理

  • wait()
    • 当前线程必须持有对象的 Monitor。
    • 调用后线程会释放锁,并进入等待队列,直到被 notify() 唤醒。
  • notify()
    • 当前线程也必须持有对象的 Monitor。
    • 唤醒等待队列中的一个线程(不会立即执行,需重新竞争锁)。

重点:JVM 规定这些行为只能发生在持有对象锁时,才能保证原子性与可见性。

为什么不加 synchronized 会报错?

如果你写:

Object lock = new Object();
lock.wait();  // ❌ 报错:IllegalMonitorStateException

会抛出异常,因为当前线程没有持有 lock 的 Monitor。

这是 Java 的安全机制,防止线程在未加锁情况下影响共享状态。


三、源码证明:OpenJDK 实现

OpenJDK 17JVM_MonitorWait 中可以看到如下代码片段:

JVM_ENTRY(void, JVM_MonitorWait(JNIEnv* env, jobject obj, jlong ms))
  ...
  if (!ObjectMonitor::is_owner(obj, current_thread)) {
      THROW(vmSymbols::java_lang_IllegalMonitorStateException());
  }
  ...
JVM_END

说明:如果当前线程不是对象的拥有者(没有进入同步块),直接抛出 IllegalMonitorStateException


四、替代方案:使用 Lock + Condition 更灵活

java.util.concurrent.locks 提供更灵活的同步机制:

✔ 示例:

import java.util.concurrent.locks.*;

class LockConditionExample {
    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();

    public void awaitMethod() throws InterruptedException {
        lock.lock();
        try {
            condition.await();  // 相当于 wait()
        } finally {
            lock.unlock();
        }
    }

    public void signalMethod() {
        lock.lock();
        try {
            condition.signal();  // 相当于 notify()
        } finally {
            lock.unlock();
        }
    }
}

官方文档:


五、官方推荐做法总结

方法是否必须同步?推荐方式参考实现
wait() / notify()✅ 是synchronizedsynchronized(obj) { ... }
await() / signal()✅ 是Lock + Conditionlock.lock() {...}

六、总结(面试 & 实战角度)

问题解答
为什么 wait() 必须在同步块中?因为只有持有对象监视器的线程才可调用它,否则 JVM 抛异常。
为什么要这样设计?避免竞态条件、确保线程状态一致性、安全地释放/等待锁。
有替代方案吗?有,ReentrantLock + Condition 提供更精细控制(多个条件队列等)。
如果不用 synchronized 会怎样?直接运行时报错:IllegalMonitorStateException,线程无锁调用是不合法的。

更多详细内容请关注其他相关文章!

发表回复 0

Your email address will not be published. Required fields are marked *