java線程notify(), wait()的使用

回顧一個比較經典的線程間協做的問題:啓動三個線程,每一個線程相應的打印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()被調用以後,才能再次進入就緒狀態,等待調度。

相關文章
相關標籤/搜索