详解Java的notifyAll()方法:唤醒在当前对象上等待的所有线程

  • Post category:Java

Java中的notifyAll()方法用于唤醒正在等待同一锁的所有线程。当一个线程调用某个对象的notifyAll()方法时,该对象处于锁定状态,此时会唤醒所有正在等待该对象锁的线程,让它们重新竞争锁。

notifyAll()的使用场景通常是在多线程环境下,要实现线程的同步,使得多个线程可以按照指定的顺序执行。下面我们来详细讲解Java的notifyAll()方法,包括使用场景、示例代码以及注意事项等。

使用场景

notifyAll()方法的使用场景通常是在多线程环境下共享资源的情况。在这种情况下,多个线程会竞争同一个资源,如果某个线程正在执行该资源,那么其他线程必须等待该线程释放对该资源的控制权才能继续执行。

例如,如果多个线程要读写同一个文件,那么每个线程必须在读写时获得对该文件的独占操作,否则就会出现读写冲突,导致数据丢失或者读取到错误的数据。在这种情况下,我们可以使用notifyAll()方法来实现线程的同步。

示例代码

下面是一个使用notifyAll()方法的简单示例。这个示例中有两个线程,一个线程是负责打印奇数,另一个线程是负责打印偶数。两个线程可以交替打印数字,直到达到设定的打印次数。

public class PrintNumber {
    private int number;
    private int count;

    public PrintNumber(int number, int count) {
        this.number = number;
        this.count = count;
    }

    public synchronized void print() {
        while (number <= count) {
            System.out.print(number + " ");
            number++;
            notifyAll();
            try {
                if (number <= count) {
                    wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        PrintNumber printer = new PrintNumber(1, 10);
        Thread t1 = new Thread(() -> {
            printer.print();
        });
        Thread t2 = new Thread(() -> {
            printer.print();
        });

        t1.start();
        t2.start();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

这个示例中,PrintNumber类表示一个打印数字的类,包含一个number变量表示当前要打印的数字,一个count变量表示要打印的总次数,以及一个print()方法负责打印数字。

print()方法中,使用了synchronized关键字来保证多线程环境下的线程安全,同时使用了whilewait()方法实现线程的等待和唤醒,以达到交替打印数字的效果。

注意事项

在使用notifyAll()方法时,需要注意以下几点:

  1. 如果没有线程在等待该对象的锁,那么notifyAll()方法不会产生任何影响,也就是说,不会有线程被唤醒。

  2. notifyAll()方法只是唤醒等待该对象的锁的所有线程,但不保证哪个线程会获得该对象的锁,获得锁的线程是由操作系统决定的。

  3. 在调用notifyAll()方法前,必须先获取该对象的锁,否则会抛出IllegalMonitorStateException异常。

  4. 在使用wait()方法时,必须在while循环中使用,以防止线程被虚假唤醒。这是因为在执行wait()方法时,会释放对象锁,而其他线程可能会获得该锁并执行部分代码,这时候唤醒了当前线程,如果不进行再次判断,那么当前线程可能会直接跳过wait()方法继续执行下去,从而导致程序出错。

结语

notifyAll()方法是Java中用于线程同步控制的关键字之一,可以实现多个线程之间的协作和数据传递。在使用notifyAll()方法时,需要注意多线程环境下的线程安全问题以及正确使用wait()方法,以达到线程同步的效果。