java中,wait和notify這兩個方法是一對,wait方法阻塞當前線程,而notify是喚醒被wait方法阻塞的線程。
首先,需要說明的是,wait和notify方法都是Object的實例方法,要執行這兩個方法,有一個前提就是,當前線程必須獲其對象的monitor(俗稱“鎖”),否則會拋出
IllegalMonitorStateException
異常,所以這兩個方法必須在同步塊代碼里面調用,經典的生產者和消費者模型就是使用這兩個方法實現的。
當前線程A獲得對象obj的monitor,然后進入臨界區(同步代碼塊),調用對象obj的wait方法,則線程A釋放obj的monitor(執行了obj的wati方法之后,就會釋放obj的monitor,不用等到執行完臨界區,因為線程A會被阻塞在當前這個位置,同時cpu的相關寄存器會記住當前位置的堆棧信息),然后進入阻塞狀態,同時線程A讓出cpu,不再參與cpu的競爭,但是還會同時wait方法內部會不斷地輪詢線程A的InterruptStatus狀態位(其實導致阻塞的方法一般都會做這個操作,就是不斷地輪詢中斷狀態位,以判斷當前被阻塞的線程是否被提前取消了),以判斷當前阻塞的狀態是否會被中斷(這里有點小矛盾,照理線程A不再參與cpu的競爭,又還不斷輪詢中斷狀態位,這個我還要研究下,有知道的可以留言評論指出,謝謝),等待其他線程調用A的notify(或notifyAll)來喚醒;然后線程B獲取的obj的monitor之后,進去臨界區,執行obj的notify方法,這時候,有點和wait方法不一樣,就是調用了obj的notify之后,不會立刻釋放obj的monitor同時喚醒線程A,而是要等到線程B執行完同步塊代碼,出了臨界區,這時候才會釋放obj的monitor,同時喚醒線程A,這時候線程A就會從新參與cpu的競爭,就有機會(因為可能其他線程也在競爭obj的monitor,如果之前有幾個線程都在等待被obj的notify喚醒,則這時候就會有幾個線程同時被喚醒,喚醒之后,因為obj的monitor同一時刻只允許一個線程擁有,所以被喚醒的幾個線程究竟誰先獲得obj的monitor繼續往下面執行呢?這個就是根據操作系統的調度算法了。一個執行完同步塊代碼,釋放obj的monitor之后,其他被喚醒的線程才會一個一個競爭獲取obj的monitor,繼續執行其wait方法后面的代碼...)獲取的obj的monitor,等到線程A重新獲得obj的monitor之后,線程A會進入臨界區,從wait方法后面繼續執行(注意:這里進入臨界區之后,不會從新從頭執行臨界區代碼塊,而會根據之前調用wait方法阻塞的時候,cpu記住的堆棧信息,會直接從wait方法后面的代碼開始繼續往下執行),直到出了臨界區,釋放obj的monitor。
這里還有一個要注意的問題就是,wait和notify調用的順序一定要注意先后,如果先調用了obj的notify,然后才調用obj的wait方法的話,則調用了wait方法被阻塞的線程則不會被喚醒,會一直處於阻塞狀態。
以下是我參考(http://www.cnblogs.com/hapjin/p/5492645.html)代碼做的實踐:
Service類
/** * Created by regis on 2017/4/23. */ public class Service { public void testMethod(Object lock) { try { synchronized (lock) { System.out.println("begin wait() ThreadName=" + Thread.currentThread().getName()); lock.wait(); if (Thread.currentThread().getName().equals("Thread-1")) { Thread.sleep(50000); } System.out.println("end wait() ThreadName=" + Thread.currentThread().getName()); } } catch (InterruptedException e) { e.printStackTrace(); } } public void synNotifyMethod(Object lock) { synchronized (lock) { System.out.println("begin notify() ThreadName=" + Thread.currentThread().getName()); lock.notifyAll(); System.out.println("end notify() ThreadName=" + Thread.currentThread().getName()); } } }
SynNotifyMethodThread類
/** * Created by regis on 2017/4/23. */ public class SynNotifyMethodThread extends Thread { private Object lock; public SynNotifyMethodThread(Object lock) { super(); this.lock = lock; } @Override public void run() { Service service = new Service(); service.synNotifyMethod(lock); } }
測試類
/** * Created by regis on 2017/4/20. */ public class Test { public static void main(String[] args) { Object lock = new Object(); ThreadA a = new ThreadA(lock); a.start(); ThreadA b = new ThreadA(lock); b.start(); try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } SynNotifyMethodThread c = new SynNotifyMethodThread(lock); c.start(); } }
ThreadA類
/** * Created by regis on 2017/4/23. */ public class ThreadA extends Thread{ private Object lock; public ThreadA(Object lock) { super(); this.lock = lock; } @Override public void run() { Service service = new Service(); service.testMethod(lock); } }
參考:http://www.cnblogs.com/hapjin/p/5492645.html