Java多线程编程:wait()、notify()和notifyAll()方法详解与实例

这是文章《Java 线程的等待、通知和通知所有示例》的第1部分(共1部分)。

Java中的Object类包含了三个最终方法,用于线程之间关于资源锁状态的通信。这些方法是wait()、notify()和notifyAll()。今天我们将深入研究Java程序中的wait、notify和notifyAll方法的使用。

Java中的等待、通知和通知所有方法

在Java中,等待、通知和通知所有的方法分别是”wait”、”notify”和”notifyAll”。调用这些方法的当前线程必须拥有该对象的监视器(锁),否则将抛出 java.lang.IllegalMonitorStateException 异常。

wait()方法

对象的wait()方法有三种变体:其中一种会无限期地等待,直到任何其他线程在同一对象上调用notify()或notifyAll()方法来唤醒当前线程;另外两种变体会在指定的时间段内将当前线程置于等待状态,超时后自动唤醒。

notify()方法

notify()方法只唤醒一个正在等待该对象的线程,被唤醒的线程将开始执行。因此,如果有多个线程在等待同一个对象,这个方法只会随机唤醒其中一个线程。具体唤醒哪个线程取决于操作系统对线程调度的实现。

notifyAll()方法

notifyAll()方法会唤醒所有正在等待该对象的线程,然而,这些被唤醒的线程中哪个先获得执行权取决于操作系统的线程调度实现。这些方法可以用于实现”生产者-消费者”问题,其中消费者线程在队列中等待对象,而生产者线程将对象放入队列并通知等待的线程。下面我们通过一个示例,展示多个线程在同一个对象上工作时,如何使用wait、notify和notifyAll方法进行协调。

Message类

这是一个会被线程调用并使用wait和notify方法的Java Bean类。

package com.Olivia.concurrency;

public class Message {
    private String msg;
    
    public Message(String str){
        this.msg=str;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String str) {
        this.msg=str;
    }

}

Waiter类

这是一个等待其他线程调用notify方法以完成其处理的类。请注意,Waiter线程通过使用synchronized块来获取Message对象的监视器。

package com.Olivia.concurrency;

public class Waiter implements Runnable{
    
    private Message msg;
    
    public Waiter(Message m){
        this.msg=m;
    }

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        synchronized (msg) {
            try{
                System.out.println(name+" 等待被通知,时间:"+System.currentTimeMillis());
                msg.wait();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            System.out.println(name+" 等待线程收到通知,时间:"+System.currentTimeMillis());
            //现在处理消息
            System.out.println(name+" 已处理: "+msg.getMsg());
        }
    }

}

Notifier类

这是一个处理Message对象并调用notify方法唤醒等待Message对象的线程的类。请注意,该类使用synchronized块来获取Message对象的监视器。

package com.Olivia.concurrency;

public class Notifier implements Runnable {

    private Message msg;
    
    public Notifier(Message msg) {
        this.msg = msg;
    }

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        System.out.println(name+" 已启动");
        try {
            Thread.sleep(1000);
            synchronized (msg) {
                msg.setMsg(name+" 通知者工作完成");
                msg.notify();
                // msg.notifyAll();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
    }

}

等待通知测试类

这是一个测试类,用于创建多个Waiter和Notifier线程并启动它们。

package com.Olivia.concurrency;

public class WaitNotifyTest {

    public static void main(String[] args) {
        Message msg = new Message("处理它");
        Waiter waiter = new Waiter(msg);
        new Thread(waiter,"waiter").start();
        
        Waiter waiter1 = new Waiter(msg);
        new Thread(waiter1, "waiter1").start();
        
        Notifier notifier = new Notifier(msg);
        new Thread(notifier, "notifier").start();
        System.out.println("所有线程都已启动");
    }

}

当我们运行上述程序时,将会看到以下输出,但程序不会正常终止,因为有两个线程正在等待Message对象,而notify()方法只唤醒其中一个线程,另一个线程仍然在等待通知。

waiter 等待被通知,时间:1356318734009
waiter1 等待被通知,时间:1356318734010
所有线程都已启动
notifier 已启动
waiter 等待线程收到通知,时间:1356318735011
waiter 已处理: notifier 通知者工作完成

如果我们在Notifier类中将notify()调用注释掉,并取消注释notifyAll()的调用,将会产生以下输出:

waiter 等待被通知,时间:1356318917118
waiter1 等待被通知,时间:1356318917118
所有线程都已启动
notifier 已启动
waiter1 等待线程收到通知,时间:1356318918120
waiter1 已处理: notifier 通知者工作完成
waiter 等待线程收到通知,时间:1356318918120
waiter 已处理: notifier 通知者工作完成

在Java中,notifyAll()方法会唤醒所有等待的线程,使它们都能继续执行,并在所有线程执行完成后正常终止程序。以上就是关于Java中wait、notify和notifyAll方法的全部内容。

bannerAds