Java多线程中wait和notify方法的同步调用原因

Java Q&A Hub 使用 GPT-4o-mini 回答

问题:

Java面试题之多线程 为什么wait和notify方法要在同步块中调用?

回答:

在Java中,wait()notify()方法是用于线程间通信的关键方法,它们都属于Object类,而不是Thread类。为了理解为什么这两个方法必须在同步块中调用,我们需要了解几个关键概念:

  1. 监视器锁(Monitor Lock):每个对象都有一个监视器锁,只有持有该对象的锁的线程才能调用wait()notify()notifyAll()方法。

  2. 线程安全wait()notify()方法的设计是为了在多线程环境中安全地进行线程间的协作。如果没有同步块,多个线程可能会同时访问共享资源,导致数据不一致或其他并发问题。

  3. 释放锁:当一个线程调用wait()方法时,它会释放持有的对象锁,并进入等待状态,直到其他线程调用notify()notifyAll()方法。这样,其他线程就可以获得该对象的锁并进行操作。

  4. 避免虚假唤醒:在调用wait()后,线程会进入等待状态,直到被唤醒。为了确保线程在被唤醒后能够正确地检查条件,通常会在循环中使用wait(),并且这个循环需要在同步块中进行,以确保在检查条件时不会被其他线程干扰。

因此,wait()notify()方法必须在同步块中调用,以确保:

  • 只有持有对象锁的线程才能调用这些方法,避免了并发问题。
  • 在调用wait()时,线程能够安全地释放锁,并在被唤醒后重新获得锁。
  • 通过同步块,确保在检查条件时不会被其他线程修改共享状态。

示例代码:

class SharedResource {
    private boolean available = false;

    public synchronized void produce() throws InterruptedException {
        while (available) {
            wait(); // 释放锁并等待
        }
        available = true;
        notify(); // 唤醒等待的线程
    }

    public synchronized void consume() throws InterruptedException {
        while (!available) {
            wait(); // 释放锁并等待
        }
        available = false;
        notify(); // 唤醒等待的线程
    }
}

在这个例子中,produce()consume()方法都在同步块中调用wait()notify(),确保了线程安全和正确的线程间通信。