Java線程間通訊
考慮經典的排隊問題,其中一個線程正在生產一些數據,另一個是消費它。為了使問題更有趣,假設生產者必須等待,直到它會產生更多的數據消費完畢之前。
在一個輪詢係統,消費者會浪費大量的CPU周期,而它等待著生產者生產。一旦生產結束了,就開始輪詢,浪費更多的CPU周期等待消費者完成,依此類推。顯然,這種情況是不希望的。
為了避免輪詢,Java包括通過下麵的方法優雅的進程間通信機製:
-
wait( ): 這個方法告訴調用線程放棄監視器和進入睡眠狀態,直到其他線程進入同一監視器和調用notify()。
-
notify( ): 這種方法喚醒第一個線程調用wait()在同一個對象上。
-
notifyAll( ): 這種方法喚醒所有調用wait()的同一個對象上的線程。最高優先級的線程將首先運行。
這些方法被實現為final的方法在Object,因此所有的類都有它們。這三種方法都隻能從一個同步的上下文中被調用。
這些方法的對象中聲明。各種形式的wait( ) 存在,使可以指定一段時間等待。
例子:
下麵的示例程序包括四個類:Q,想同步隊列,Producer,也就是生產隊列的條目線程對象;Consumer,即消耗隊列的條目線程對象和PC,tiny類創建一個Q,生產者和消費者。
寫這個程序在Java中正確的方法是使用wait()和notify()方法,以在兩個方向的信號,如下所示:
class Q { int n; boolean valueSet = false; synchronized int get() { if(!valueSet) try { wait(); } catch(InterruptedException e) { System.out.println("InterruptedException caught"); } System.out.println("Got: " + n); valueSet = false; notify(); return n; } synchronized void put(int n) { if(valueSet) try { wait(); } catch(InterruptedException e) { System.out.println("InterruptedException caught"); } this.n = n; valueSet = true; System.out.println("Put: " + n); notify(); } } class Producer implements Runnable { Q q; Producer(Q q) { this.q = q; new Thread(this, "Producer").start(); } public void run() { int i = 0; while(true) { q.put(i++); } } } class Consumer implements Runnable { Q q; Consumer(Q q) { this.q = q; new Thread(this, "Consumer").start(); } public void run() { while(true) { q.get(); } } } public class PCFixed { public static void main(String args[]) { Q q = new Q(); new Producer(q); new Consumer(q); System.out.println("Press Control-C to stop."); } }
內部的get(),wait()調用。這將導致其執行暫停,直到生產者通知有些數據已準備就緒。
當發生這種情況,執行裡麵的get( ) 恢複。該數據已獲得後,get() 調用notify()。這告訴生產者,這都是可以的把更多的數據在隊列中。
內部的put(),wait()暫停執行,直到消費者已經從隊列中刪除的項目。當恢複執行,數據的下一個項目被放入隊列中,notify()被調用。這就告訴消費者,它現在應該將其刪除。
下麵是這個程序,它顯示了乾淨的同步行為的一些輸出:
Put: 1 Got: 1 Put: 2 Got: 2 Put: 3 Got: 3 Put: 4 Got: 4 Put: 5 Got: 5