Thread的wait方法理解
wait方法是Object物件的方法。執行緒與鎖是分不開的,執行緒的同步、等待、喚醒都與物件鎖是密不可分的。wait方法會將當前執行緒放入wait set,等待被喚醒,並放棄lock物件上的所有同步宣告,當前執行緒會因為執行緒排程的原因處於休眠狀態而不可用。只有通過以下四個方法可以主動喚醒:
1. notify
2. notifyAll
3. Thread.interrupt()
4. 等待時間過完。
當執行緒被喚醒後,執行緒就從wait set中移除了並且重新獲得執行緒排程能力,同時像其它執行緒一樣持有object的鎖。
一段synchronized的程式碼被一個執行緒執行之前,他要先拿到執行這段程式碼的許可權,
在Java裡邊就是拿到某個同步物件的鎖(一個物件只有一把鎖);
如果這個時候同步物件的鎖被其他執行緒拿走了,他(這個執行緒)就只能等了(執行緒阻塞在鎖池等待佇列中)。
取到鎖後,他就開始執行同步程式碼(被synchronized修飾的程式碼);
執行緒執行完同步程式碼後馬上就把鎖還給同步物件,其他在鎖池中等待的某個執行緒就可以拿到鎖執行同步程式碼了。
這樣就保證了同步程式碼在統一時刻只有一個執行緒在執行。
這裡就需要補充一下物件鎖和類鎖的區別。
事實上,synchronized修飾非靜態方法、同步程式碼塊的synchronized (this)用法和synchronized (非this物件)的用法鎖的是物件,執行緒想要執行對應同步程式碼,需要獲得物件鎖。
執行緒正常結束後,會使以這個執行緒物件執行的wait()等待,退出等待狀態!而如果在執行wait()之前,執行緒已經結束了,則這個wait就沒有程式喚醒了。
原碼裡的join()方法,實際上就是執行的 wait(). 需要執行join的執行緒執行join方法,實際上是在此執行緒上呼叫了需要加入的執行緒物件的wait()方法,加入的執行緒執行完後,自然從wait退出了。
到此,就得出了我的結論:
1 執行緒物件的wait()方法執行後,可以不用其notify()方法退出,會線上程結束後,自動退出。
2 執行緒間的等待喚醒機制,最好不要用執行緒物件做同步鎖!
首先我們看一個例項:
public class TestDemo {
public static void main(String []args) throws InterruptedException{
MyThread myThread = new MyThread();
System.out.println("before");
myThread.start();
System.out.println("after" );
}
static class MyThread extends Thread{
public void run(){
synchronized (this) {
for(int i=0;i<3;i++){
System.out.println("number:"+ i);
}
}
}
}
}
輸出的結果是:
before
after
number:0
number:1
number:2
首先是Main執行緒具有搶佔cpu資源,然後執行完後,在開始執行子執行緒。
例項2:
public class TestDemo {
public static void main(String []args) throws InterruptedException{
MyThread myThread = new MyThread();
System.out.println("before");
myThread.start();
synchronized (myThread) {
myThread.wait();
}
System.out.println("after");
}
static class MyThread extends Thread{
public void run(){
synchronized (this) {
for(int i=0;i<3;i++){
System.out.println("number:"+ i);
}
}
}
}
}
before
number:0
number:1
number:2
after
我們呼叫wait方法,Main執行緒會釋放當前鎖,進入wait set。然後子執行緒開始執行,當子執行緒執行完畢後,會把鎖歸還。