我們先看一段代碼:
/**
* 計算輸出其他線程鎖計算的數據
*
*/
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異常。