线程间协作——wait & notify & notifyAll

来源:互联网 发布:c语言全集 编辑:程序博客网 时间:2024/05/20 12:22

概要描述

在 Java 中可以用 wait、notify 和 notifyAll 来实现线程间的通信。尽管关于wait和notify的概念很基础,它们也都是Object类的函数,但用它们来写代码却并不简单。

wait, notify, notifyAll 都是基类Object的方法,而不属于Thread,这让习惯了调用Thread.sleep()使线程阻塞的同学感到奇怪。不过这样设计是有道理的,因为这些方法操作的锁(monitor)也是对象的一部分。可见,与sleep不同,通过调用共享对象的wait方法使当前线程等待;通过调用对象的notify, notifyAll 方法唤醒该对象上的等待线程。

先来看官方文档,Java doc对wait方法的描述:

public final void wait() throws InterruptedExceptionCauses the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object. In other words, this method behaves exactly as if it simply performs the call wait(0).The current thread must own this object's monitor. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object's monitor to wake up either through a call to the notify method or the notifyAll method. The thread then waits until it can re-obtain ownership of the monitor and resumes execution.As in the one argument version, interrupts and spurious wakeups are possible, and this method should always be used in a loop:     synchronized (obj) {         while (<condition does not hold>)             obj.wait();         ... // Perform action appropriate to condition     }This method should only be called by a thread that is the owner of this object's monitor. See the notify method for a description of the ways in which a thread can become the owner of a monitor.Throws:    IllegalMonitorStateException - if the current thread is not the owner of the object's monitor.    InterruptedException - if any thread interrupted the current thread before or while the current thread was waiting for a notification. The interrupted status of the current thread is cleared when this exception is thrown. 

Java doc对notify的描述:

public final void notify()Wakes up a single thread that is waiting on this object's monitor. If any threads are waiting on this object, one of them is chosen to be awakened. The choice is arbitrary and occurs at the discretion of the implementation. A thread waits on an object's monitor by calling one of the wait methods.The awakened thread will not be able to proceed until the current thread relinquishes the lock on this object. The awakened thread will compete in the usual manner with any other threads that might be actively competing to synchronize on this object; for example, the awakened thread enjoys no reliable privilege or disadvantage in being the next thread to lock this object.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 objects of type Class, by executing a synchronized static method of that class. Only one thread at a time can own an object's monitor.Throws:    IllegalMonitorStateException - if the current thread is not the owner of this object's monitor.

生产者-消费者示例:

/** *  */public class Producer extends Thread {    private volatile Queue<Integer> queue;    private int maxSize;    public Producer(Queue<Integer> queue, int maxSize) {        this.queue = queue;        this.maxSize = maxSize;    }    @Override    public void run() {        while (true) {            //wait,notify方法必须在同步代码中运行            synchronized (queue) {                //条件一定在循环中判断,以防死锁                while (queue.size() == maxSize) {                    try {                             System.out.println(Thread.currentThread().getName() + " wait.");                        queue.wait();                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }                int i = new Random().nextInt();                System.out.println(Thread.currentThread().getName() + " produce: " + i);                queue.add(i);                queue.notifyAll();            }        }    }}

消费者:

 /** *  */public class Consumer extends Thread {    private volatile Queue<Integer> queue;    private int maxSize;    public Consumer(Queue<Integer> queue, int maxSize) {        this.queue = queue;        this.maxSize = maxSize;    }    @Override    public void run() {        while (true) {            synchronized (queue) {                while (queue.isEmpty()) {                    try {                        System.out.println(Thread.currentThread().getName() + " wait.");                        queue.wait();                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }                System.out.println(Thread.currentThread().getName() + " consume: " + queue.remove());                queue.notifyAll();            }        }    }}

main方法:

/** *  */public class WaitNotifyMain {    private volatile static Queue<Integer> queue = new LinkedList<>();    /**     * @param args     */    public static void main(String[] args) {        Producer producer = new Producer(queue, 10);        producer.setName("Producer");//设置线程名称        Consumer consumer = new Consumer(queue, 10);        consumer.setName("Consumer");        producer.start();        consumer.start();    }}

打印输出:

Producer produce: -2017386252Producer produce: 1186339917Producer produce: -674757828Producer produce: 605757848Producer produce: -539314860Producer produce: 490590935Producer produce: -845855520Producer produce: -1459720588Producer produce: 1274488529Producer produce: 55225134Producer wait.Consumer consume: -2017386252Consumer consume: 1186339917Consumer consume: -674757828Consumer consume: 605757848Consumer consume: -539314860Consumer consume: 490590935Consumer consume: -845855520Consumer consume: -1459720588Consumer consume: 1274488529Consumer consume: 55225134Consumer wait.Producer produce: 673397316Producer produce: -1176693368Producer produce: -1707265532Producer produce: -1614197913Producer produce: 306171031Producer produce: -1646438955Producer produce: 1141572321Producer produce: 1235215288Producer produce: -692805724Producer produce: -2131184778Producer wait.Consumer consume: 673397316Consumer consume: -1176693368

总结

  1. wait, notify, notifyAll 是共享对象上的调用,而不是线程对象的调用。

  2. wait, notify, notifyAll一定要在共享对象同步方法或同步代码块中执行,否则会在运行时抛出IllegalMonitorStateException的异常。因为wait, notify, notifyAll包含了对共享对象锁的操作,所以之前一定要先synchronized获取对象锁。

  3. 在共享对象上调用wait()时,当前线程进入等待状态, 并释放刚获取的对象锁(Thread.sleep()是不释放锁的),让出CPU, 此时,其他线程可以调用共享对象的同步方法或代码块。

  4. 唤醒线程在共享对象上执行notify会随机唤醒该对象的其中之一等待线程;唤醒线程在共享对象上执行notifyAll会唤醒该对象上的所有等待线程;这里要着重注意两点

    A,唤醒线程执行完共享对象的notify或notifyAll方法后,仍然要执行完synchronized修饰的同步代码块中后面的代码才能释放对象锁,因此,通常notify后面尽量减少执行代码,让对象锁尽快释放。

    B, 唤醒是指线程ready的状态,尚未运行,共享对象的锁被唤醒线程释放后,ready状态的线程跟普通线程一样需要竞争共享对象的锁,执行同步代码块中wait()后面的代码。

  5. 永远在循环(loop)里调用 wait 和 notify, notifyAll,不是在 If 语句,避免死锁情况发生。

基于以上认知,下面这个是使用wait和notify函数的规范代码模板:

// The standard idiom for calling the wait method in Java synchronized (sharedObject) {     while (condition) {     sharedObject.wait();         // (Releases lock, and reacquires on wakeup)     }     // do action based upon condition e.g. take or put into queue }

在while循环里使用wait的目的,是在线程被唤醒的前后都持续检查条件是否被满足。如果条件并未改变,wait被调用之前notify的唤醒通知就来了,那么这个线程并不能保证被唤醒,有可能会导致死锁问题。

阅读全文
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 物流号码留错了怎么办 12306身份信息被注册怎么办 12306注册身份信息重复怎么办 12306账号被注册了怎么办 高铁账号忘记了怎么办 铁路12306网站密码错误怎么办 网上买火车票密码忘了怎么办 快递没收到点了确认收货怎么办 快递没收到自动确认收货怎么办 房地产股市汇率一齐暴跌怎么办 尼日利亚落地签过期了怎么办 期货亏光了所有怎么办 期货钱亏完了该怎么办 做黄金亏损500万怎么办 炒黄金被骗35万怎么办 淘宝发货填错单号怎么办 发货单号填错了怎么办 发快递忘了单号怎么办 国际物流查不到物流怎么办 纸币上印邪教该怎么办 钥匙掉到电梯缝里怎么办 汽车电子钥匙铜线折一根怎么办 防盗门的锁不好开怎么办 同学帮刷饭卡说不用还钱了怎么办 em231电源指示灯不亮怎么办 运行广联达卡住怎么办 马桶被粪便(大便)堵了怎么办 子宫壁厚12mm怎么办 管子太多每次洗澡都是冷水怎么办 热水冷水装反了怎么办 大树被高锰酸钾灌溉了怎么办会死吗 防盗门门被锁了怎么办 门被里面反锁了怎么办 门里面被锁了怎么办 被锁在门里怎么办 门锁住了没钥匙怎么办 车被别人锁住了怎么办 汽车轱辘被锁了怎么办 小车轮胎被锁了怎么办 国防光缆无明显标识被挖断怎么办 临工210挖掘机柴油进气怎么办