上一節:程序、線程一張圖
wait()方法阻塞線程
先說一下線程的幾個狀态,也乘坐線程的生命周期
線程分為五個生命周期,分别是:
建立 – 就緒 – 運作 – 阻塞 – 死亡
個人了解:
建立狀态和普通對象被建立一樣,隻是聲明這麼一個東西
就緒狀态下,線程可以參與鎖的競争,等待擷取cpu的執行權
運作狀态,那就是線程在執行了,可以了解為一個被cpu允許執行的方法
阻塞狀态,就是線程無法主動參與鎖的競争,需要被線程排程器喚醒之後才被允許參與鎖競争
死亡狀态,不存在于程序中了
再來看一張圖:
線程A、B、C都需要操作對象O
在第一次鎖的競争中,線程A搶到了對象O的鎖,那麼線程B和線程C都需要等待線程A釋放對象O的鎖才可以去擷取對對象O的操作權;
線程A在執行的過程中調用了對象O的wait()方法,此時線程A就釋放了對象O的鎖并釋放了cpu的執行權,進入對象O的等待隊列,線程A此時處于阻塞狀态,這個時候線程B和線程C開始競争,然後線程B搶到對象O的鎖;
線程B在執行的過程中又調用了對象O的wait()方法,此時,線程B也釋放cpu的執行權和對象O的鎖,由于A處于阻塞狀态是以它不參與鎖的競争,那麼線程C此時競争到了對象O的鎖并開始執行;
線上程C執行的過程中調用了對象O的notify()方法,此時線程排程器會随機的去喚醒線程A或者線程B,注意,不是全部都喚醒,而是隻喚醒一個,那麼被喚醒的那個線程從阻塞狀态恢複到就緒狀态,也就是這個被喚醒的線程接下來可以參與對象O鎖的競争了,另外沒有被喚醒的線程依舊處于阻塞狀态;
假如線程C調用的并不是notify()方法而是notifyAll()方法,那麼線程排程器會喚醒所有在對象O等待隊列的線程,也就是将線程A和線程B都喚醒,這個時候線程A和線程B都從阻塞狀态恢複到就緒狀态,也就是可以參與對象O鎖的競争當中了;
那麼線程C在執行的過程當中,先執行對象O的notifyAll()方法,再執行對象O的wait()方法,這個時候線程C就進入了對象O的等待隊列處于阻塞狀态,被喚醒的線程A和線程B開始競争對象O的鎖,擷取到鎖的那個線程,會從之前調用wait()方法的地方繼續執行,直到下一次釋放對象O的鎖或者線程執行完畢。
總結:調用wait()方法會釋放對象的螢幕鎖,讓目前線程進入對象的等待隊列,處于阻塞狀态,并釋放cpu的執行權,直到其他線程調用該對象的notify()方法【可能被喚醒】或者notifyAll()方法【沒有意外,一定被喚醒】才恢複就緒狀态,可以參與對象鎖的競争。
下面是通過代碼驗證:
public class WaitTest {
private Object object = new Object();
public void threads() throws InterruptedException {
Thread thread1 = new Thread(() -> {
synchronized (object){
try {
System.out.println("線程1 擷取對象鎖");
System.out.println("線程1 阻塞,釋放鎖");
object.wait();
System.out.println("線程1 被喚醒,執行完畢");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread thread2 = new Thread(() -> {
synchronized(object){
try {
System.out.println("線程2 擷取對象鎖");
System.out.println("線程2 阻塞,釋放鎖");
object.wait();
System.out.println("線程2 被喚醒,執行完畢");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread thread3 = new Thread(() -> {
synchronized(object){
System.out.println("線程3 擷取對象鎖");
System.out.println("線程3 調用notify");
object.notify();
System.out.println("線程3 執行完畢,釋放鎖");
}
});
thread1.start();
thread2.start();
//讓線程3休眠兩秒再開始執行,保證線程1和2先進入object的等待隊列
Thread.sleep(2000);
thread3.start();
}
public static void main(String[] args) throws InterruptedException {
WaitTest waitTest = new WaitTest();
waitTest.threads();
}
}
建立一個Object對象,,threads()方法内建立了三個線程
線程1和2調用wait()方法阻塞自己 線程3用來喚醒
第一種情況:
線程1和線程2調用wait() 線程3調用notify()
執行結果:
執行多次後的結果,,線上程3執行完畢後要麼線程1執行結束要麼線程2執行結束然後主線程就結束了,說明線上程3調用notify()方法後隻喚醒了線程1、2中的其中一個去執行,另外一個依舊在阻塞狀态中,沒有參與鎖的競争。
第二種情況:
線程1和線程2調用wait() 線程3調用notifyAll()
執行結果:
線上程3調用notifyAll後,處于阻塞的線程1和2都被喚醒,并且執行了,不過執行的順序是不固定的,,這是因為線程需要競争到對象鎖,是以在調用notifyAll後具體是哪個阻塞線程繼續執行,這個是不确定的;
第三種情況:
線程1,2調用wait(long time),線程3調用notify()
線程1中的wait()現在改成wait(long time)方法
讓線程1等待一秒,看看執行結果:
線程1在wait(1000)後釋放了鎖,并且線程2擷取到了并調用了wait方法進入了對象的等待隊列,線上程3還沒有擷取到對象鎖前,線程1就被喚醒了
說明,wait(long time)和wait方法一樣,在被調用後,線程會釋放對象的鎖讓其他線程去競争,然後在定義的time到了之後自動被喚醒從阻塞狀态變為就緒狀态。