详解Java的notifyAll()方法:唤醒正在等待此对象监视器的所有线程

  • Post category:Java

Java中的notifyAll()方法是一个对象级别的方法,用来唤醒等待在该对象上的所有线程。当一个线程在一个对象上调用wait()方法被阻塞时,其他线程可以在该对象上调用notify()方法将其唤醒。但是,如果有多个线程等待在该对象上,调用notify()方法只能随机唤醒其中一个线程。为了唤醒等待在该对象上的所有线程,我们需要使用notifyAll()方法。

notifyAll()方法具有以下特性:

  • notifyAll()方法唤醒该对象上所有被阻塞的线程,从而达到通知所有线程的效果。
  • 唤醒的线程会进入到对象的锁池中竞争锁,竞争成功的线程会执行,竞争失败的线程仍然被阻塞
  • 调用notifyAll()方法只有在拥有该对象的引用的synchronized块或方法中才能被调用

以下是示例代码说明:

  1. 基本使用示例
public class notifyAllExample {
    public static void main(String[] args) {
        final Object lock = new Object();
        new Thread(() -> {
            synchronized (lock) {
                try {
                    System.out.println("Thread 1 is waiting...");
                    lock.wait();
                    System.out.println("Thread 1 has been notified...");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread(() -> {
            synchronized (lock) {
                try {
                    System.out.println("Thread 2 is waiting...");
                    lock.wait();
                    System.out.println("Thread 2 has been notified...");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread(() -> {
            synchronized (lock) {
                System.out.println("Thread 3 is notifying...");
                lock.notifyAll();
            }
        }).start();
    }
}

在以上示例中,我们创建了3个线程,其中前两个线程在锁对象上调用wait()方法被阻塞。第三个线程在锁对象上调用notifyAll()方法唤醒所有的线程,这样被阻塞的线程就会继续执行,输出下面的结果:

Thread 1 is waiting...
Thread 2 is waiting...
Thread 3 is notifying...
Thread 1 has been notified...
Thread 2 has been notified...
  1. 解决死锁
public class DeadlockSolution {
    Object lock1 = new Object();
    Object lock2 = new Object();

    public void method1() {
        synchronized (lock1) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (lock2) {
                System.out.println("method1");
            }
        }
    }

    public void method2() {
        synchronized (lock2) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (lock1) {
                System.out.println("method2");
            }
        }
    }

    public static void main(String[] args) {
        DeadlockSolution d = new DeadlockSolution();
        new Thread(() -> d.method1()).start();
        new Thread(() -> d.method2()).start();

        synchronized (d.lock1){
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            d.lock1.notifyAll();
        }
    }
}

在以上示例中,我们创建了两个方法method1()method2(),两个方法分别持有对象锁lock1lock2,并且相互依赖。我们同时创建了两个线程分别去执行method1()method2(),这样就形成了死锁。我们在main方法中通过调用notifyAll()方法,通知所有被阻塞的线程,解除死锁状态,输出结果:

method1
method2

我们可以看到,线程成功地执行了两个方法,这样就成功解除了代码的死锁状态。