回顧一個比較經典的線程間協做的問題:啓動三個線程,每一個線程相應的打印10遍A,10遍B,10遍C。要求三個線程交替執行,輸處10遍ABC。java
用Object類的notify(), wait()方法可實現上述要求。ide
Object.notify()能夠喚醒一個線程,使之進入就緒狀態,等待獲取對象鎖後運行。this
Object.wait()方法可使一個線程進入阻塞狀態,而後釋放對象鎖,等待被notify()方法喚醒。spa
由於notify()和wait()方法都跟對象鎖相關,因此必須在同步塊裏面被調用纔有效。即都是以下形式:線程
synchronized(Obj){ try{ Obj.wait(); } catch (Exception e){ ... } Obj.notify; }
回看題目自己。要交替打印A,B,C,創建須要三個線程,三個對象。單個打印時,線程須要獲得兩個對象鎖。code
public class WatiNotifyTest { public static void main(String[] args) { Object obj1 = new Object(); Object obj2 = new Object(); Object obj3 = new Object(); MyThread myThread1 = new MyThread("A", obj1, obj2); MyThread myThread2 = new MyThread("B", obj3, obj1); MyThread myThread3 = new MyThread("C", obj2, obj3); myThread1.start(); myThread2.start(); myThread3.start(); } } class MyThread extends Thread { private String name; private Object a; private Object b; public MyThread(String name, Object a, Object b){ this.name = name; this.a = a; this.b = b; } @Override public void run() { int count = 10; while(count > 0){ synchronized(a){ synchronized (b) { System.out.println(name);//print "A" or "B" or "C". count--; /****************/ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } /****************/ b.notify();//wake up the thread which is waiting for the lock of "b". } try { a.wait();//let current thread release the lock on a. } catch (InterruptedException e) { e.printStackTrace(); } } } } }
線程對象MyThread的run方法會執行10次循環,每次循環都打印出當前線程的name(A或B或C)。而兩個synchronized塊是實現交替打印的關鍵。對象
從main方法入手。創建三個對象,obj1,ojb2,obj3,共有三把對象鎖。線程myThread1啓動,執行myThread1的run方法。同理,myThread2,myThread3啓動,執行各自的run方法。對於myThread1來講,a就是obj1,b就是obj2。同理,對於myThread2來講,a就是obj3,b就是obj1。以下是程序運行步驟:資源
一、myThread1進入while循環,進入synchronized塊。myThread1得到了obj1和obj2兩個對象鎖。而後輸出"A",sleep 10毫秒。調用obj2.notify(),喚醒在等待obj2的線程;調用obj1.wait(),釋放obj1的鎖。同步
二、在上述myThread1執行的過程當中,myThread2進入while循環,然而它暫時只能進入第一個synchronized塊,由於它須要obj1的鎖才能進入第二個synchronized塊,而此時obj1的鎖在myThread1手中。當myThread1調用obj1.wait()以後,myThread2進入第二個synchronized塊,得到obj3和obj1兩個對象鎖,而後輸出"B",sleep 10毫秒,調用obj1.notify(),喚醒在等待obj1的線程;調用obj3.wait(),釋放obj3的鎖.it
三、在myThread1執行完obj2.notify()並退出第二個synchronized塊的時候,myThread3獲得了obj2的對象鎖,進入第一個synchronized塊。當myThread2執行完obj3.wait()以後,得到obj3的對象鎖,進入第二個synchronized塊。而後輸出"C",sleep 10毫秒。調用obj3.notify(),喚醒在等待obj3的線程;調用obj2.wait(),釋放obj2的鎖。
四、myThread1在第一次循環執行完後進入waiting狀態,等待的是obj1的鎖,以便進入第一個synchronized塊。而當myThread2執行完畢的時候,調用了obj1.notify(),此時myThread1再次進入第一個synchronized塊。等待myThread3執行完obj2.wait()釋放obj2的鎖以後,myThread1進入第二個synchronized塊。而後重複1,2,3,4的步驟。
代碼中/*****/之間的部分很重要,它保證了三個線程的執行順序不受JVM隨機調度的影響。
從上面例子能夠看出,wait()和notify()方法,主要是控制對象的使用權的。notify()至關於告訴在waiting狀態的線程說,某某資源我已經用完了,你能夠來使用了。wait()方法至關於自動讓出資源的使用權,進入阻塞狀態(相似sleep,可是sleep會保留對象鎖)。
注意wait()和notify()通常是一塊兒使用的。在調用了Object.wait()的線程,只有在Object.notify()被調用以後,才能再次進入就緒狀態,等待調度。