線程中的wait() 與 鎖的關係

我們先看一段代碼:

/** 
* 計算輸出其他線程鎖計算的數據
* 
*/ 
public class ThreadA {
    public static void main(String[] args) throws InterruptedException{
        ThreadB b = new ThreadB();
        //啓動計算線程
        b.start(); 
        //線程A擁有b對象上的鎖。線程爲了調用wait()或notify()方法,該線程必須是那個對象鎖的擁有者
        synchronized (b) {
            System.out.println("等待對象b完成計算。。。");
            //當前線程A等待
            b.wait();
            System.out.println("b對象計算的總和是:" + b.total);
        } 
    } 
}

 

/** 
* 計算1+2+3 ... +100的和
* 
*/ 
class ThreadB extends Thread {
    int total; 

    public void run() {
        synchronized (this) {
            for (int i = 0; i < 101; i++) {
                total += i; 
            } 
            //(完成計算了)喚醒在此對象監視器上等待的單個線程,在本例中線程A被喚醒
            notify(); 
            System.out.println("計算完成");
        } 
    } 
}

 

執行結果:

等待對象b完成計算。。。
計算完成
b對象計算的總和是:5050

 

如果我們將b.wait()去掉呢?結果如下:

等待對象b完成計算。。。
b對象計算的總和是:0
計算完成

 

上述的結果表明,當去掉b.wait()時,新啓動的線程ThreadB與主線程ThreadA是各自執行的,沒有線程等待的現象。

我們想要的效果是,當線程ThreadB完成計算之後,再去取計算後的結果。所以使用了b.wait()來讓主線程等待。

那爲什麼是使用b.wait(),而不是Thread.currentThread.wait(),或者其他的呢?

 

如果我們將b.wait()替換成Thread.currentThread.wait(),將會得到如下的結果:

Exception in thread "main" java.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:485)
at pa.com.thread.ThreadA.main(ThreadA.java:18)
等待對象b完成計算。。。
計算完成

 

替換的代碼Thread.currentThread.wait()好像理所當然應該如我們預期的正確啊,讓當前線程處於等待狀態,讓其他線程先執行。

我們忽略了一個很重要的問題:線程與鎖是分不開的,線程的同步、等待、喚醒都與對象鎖是密不可分的。

線程ThreadA持有對象b的鎖,我們要使用這把鎖去讓線程釋放鎖,從而讓其他的線程能搶到這把鎖。

從我們的程序來分析就是:線程ThreadA首先持有鎖對象b,然後調用b.wait()將對象鎖釋放,線程ThreadB爭搶到對象鎖b,從而執行run()方法中的計算,計算完了之後使用notify()喚醒主線程ThreadA,ThreadA得以繼續執行,從而得到了我們預期的效果。

(之所以ThreadB的對象鎖也是b,是因爲synchronized(this)中的this指向的就是ThreadB的實例b)

Thread.currentThread.wait()調用的是當前線程對象(即主線程ThreadA)的wait()方法,當前線程對象ThreadA是沒有被加鎖的,它只是獲取了對象鎖b。我基本沒有看到過這樣的調用,一般使用的是鎖對象的wait(),本例中爲b.wait()

 

 

順帶講一下wait()與sleep()的區別。

如果我們將b.wait()換成Thread.sleep(1000),則會出現如下的結果:

等待對象b完成計算。。。
b對象計算的總和是:0
計算完成

從執行結果可以看出,Thread.sleep(1000)只是讓主線程ThreadA睡眠了1秒鐘,而並沒有釋放對象鎖,所以在主線程ThreadA睡眠的過程中,ThreadB拿不到對象鎖,從而不能執行。

所以我們也就得出瞭如下的結論:

wait()方法是讓線程釋放對象鎖,讓其他線程拿到鎖之後去優先執行,當其他全程喚醒wait()中的線程 或者 拿到對象鎖的線程都執行完釋放了對象鎖之後,wait()中的線程纔會再次拿到對象鎖從而執行。

sleep()方法是讓線程睡眠,此時並沒有釋放對象鎖,其他想要拿到睡眠線程的對象鎖的線程也就拿不到相應的對象鎖,從而不能搶在它前面執行。

 

 

補:

wait、notify和notifyAll方法是Object類的final native方法。所以這些方法不能被子類重寫,Object類是所有類的超類,因此在程序中有以下三種形式調用wait等方法。

wait();//方式1:
this.wait();//方式2:
super.wait();//方式3

void wait()

導致線程進入等待狀態,直到它被其他線程通過notify()或者notifyAll喚醒。該方法只能在同步方法中調用。如果當前線程不是鎖的持有者,該方法拋出一個IllegalMonitorStateException異常。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章