java中的wait、notify、notifyAll

在java中,線程間的通訊可使用wait、notify、notifyAll來進行控制。從名字就能夠看出來這3個方法都是跟多線程相關的,可是可能讓你感到吃驚的是:這3個方法並非Thread類或者是Runnable接口的方法,而是Object類的3個本地方法。java

其實要理解這一點也並不難,調用一個Object的wait與notify/notifyAll的時候,必須保證調用代碼對該Object是同步的,也就是說必須在做用等同於synchronized(obj){......}的內部纔可以去調用obj的wait與notify/notifyAll三個方法,不然就會報錯:多線程

java.lang.IllegalMonitorStateException:current thread not owner

也就是說,在調用這3個方法的時候,當前線程必須得到這個對象的鎖,那麼這3個方法就是和對象鎖相關的,因此是屬於Object的方法而不是Thread,由於不是每一個對象都是Thread。因此咱們在理解wait、notify、notifyAll以前,先要了解如下對象鎖。ide

多個線程都持有同一個對象的時候,若是都要進入synchronized(obj){......}的內部,就必須拿到這個對象的鎖,synchronized的機制保證了同一時間最多隻能有1個線程拿到了對象的鎖,以下圖:this

 

3個線程競爭對象A鎖線程

下面咱們來看一下這3個方法的做用:
wait:線程自動釋放其佔有的對象鎖,並等待notify
notify:喚醒一個正在wait當前對象鎖的線程,並讓它拿到對象鎖
notifyAll:喚醒全部正在wait前對象鎖的線程code

notify和notifyAll的最主要的區別是:notify只是喚醒一個正在wait當前對象鎖的線程,而notifyAll喚醒全部。值得注意的是:notify是本地方法,具體喚醒哪個線程由虛擬機控制;notifyAll後並非全部的線程都能立刻往下執行,它們只是跳出了wait狀態,接下來它們還會是競爭對象鎖。對象

下面經過一個經常使用生產者、消費者的例子來講明。
消息實體類:接口

package com.podongfeng;

/**
 * Title: Message.class<br>
 * Description: 消息實體<br>
 * Create DateTime: 2016年04月17日 下午1:27 <br>
 *
 * @author podongfeng
 */
public class Message {
}

生產者:ip

package com.podongfeng;

import java.util.ArrayList;
import java.util.List;

/**
 * Title: Producer.class<br>
 * Description: 消息生產者<br>
 * Create DateTime: 2016年04月17日 下午1:28 <br>
 *
 * @author podongfeng
 */
public class Producer extends Thread {

    List<Message> msgList = new ArrayList<>();

    @Override public void run() {
        try {
            while (true) {
                Thread.sleep(3000);
                Message msg = new Message();
                synchronized(msgList) {
                    msgList.add(msg);
                    msgList.notify(); //這裏只能是notify而不能是notifyAll,不然remove(0)會報java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Message waitMsg() {
        synchronized(msgList) {
            if(msgList.size() == 0) {
                try {
                    msgList.wait();
                } catch(InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return msgList.remove(0);
        }
    }
}

消費者:rem

package com.podongfeng;

/**
 * Title: Consumer.class<br>
 * Description: 消息消費者<br>
 * Create DateTime: 2016年04月17日 下午1:28 <br>
 *
 * @author podongfeng
 */
public class Consumer extends Thread {

    private Producer producer;

    public Consumer(String name, Producer producer) {
        super(name);
        this.producer = producer;
    }

    @Override public void run() {
        while (true) {
            Message msg = producer.waitMsg();
            System.out.println("Consumer " + getName() + " get a msg");
        }
    }

    public static void main(String[] args) {
        Producer p = new Producer();
        p.start();
        new Consumer("Consumer1", p).start();
        new Consumer("Consumer2", p).start();
        new Consumer("Consumer3", p).start();
    }
}

消費者線程調用waitMsg去獲取一個消息實體,若是msgList爲空,則線程進入wait狀態;生產這線程每隔3秒鐘生產出體格msg實體並放入msgList列表,完成後,調用notify喚醒一個消費者線程去消費。

最後再次提醒注意:
wait、notify、notifyAll並非Thread類或者是Runnable接口的方法,而是Object類的3個本地方法。
在調用這3個方法的時候,當前線程必須得到這個對象的鎖

做者:破東風CAFEBABY 連接:https://www.jianshu.com/p/f7d4819b7b24 來源:簡書 簡書著做權歸做者全部,任何形式的轉載都請聯繫做者得到受權並註明出處。

相關文章
相關標籤/搜索