在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 來源:簡書 簡書著做權歸做者全部,任何形式的轉載都請聯繫做者得到受權並註明出處。