天天看點

wait()--阻塞線程

上一節:程序、線程一張圖

wait()方法阻塞線程

先說一下線程的幾個狀态,也乘坐線程的生命周期

線程分為五個生命周期,分别是:

建立 – 就緒 – 運作 – 阻塞 – 死亡

個人了解:

建立狀态和普通對象被建立一樣,隻是聲明這麼一個東西

就緒狀态下,線程可以參與鎖的競争,等待擷取cpu的執行權

運作狀态,那就是線程在執行了,可以了解為一個被cpu允許執行的方法

阻塞狀态,就是線程無法主動參與鎖的競争,需要被線程排程器喚醒之後才被允許參與鎖競争

死亡狀态,不存在于程序中了

再來看一張圖:

wait()--阻塞線程

線程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()

執行結果:

wait()--阻塞線程
wait()--阻塞線程

執行多次後的結果,,線上程3執行完畢後要麼線程1執行結束要麼線程2執行結束然後主線程就結束了,說明線上程3調用notify()方法後隻喚醒了線程1、2中的其中一個去執行,另外一個依舊在阻塞狀态中,沒有參與鎖的競争。

第二種情況:

線程1和線程2調用wait() 線程3調用notifyAll()

wait()--阻塞線程

執行結果:

wait()--阻塞線程
wait()--阻塞線程

線上程3調用notifyAll後,處于阻塞的線程1和2都被喚醒,并且執行了,不過執行的順序是不固定的,,這是因為線程需要競争到對象鎖,是以在調用notifyAll後具體是哪個阻塞線程繼續執行,這個是不确定的;

第三種情況:

線程1,2調用wait(long time),線程3調用notify()

線程1中的wait()現在改成wait(long time)方法

wait()--阻塞線程

讓線程1等待一秒,看看執行結果:

wait()--阻塞線程

線程1在wait(1000)後釋放了鎖,并且線程2擷取到了并調用了wait方法進入了對象的等待隊列,線上程3還沒有擷取到對象鎖前,線程1就被喚醒了

說明,wait(long time)和wait方法一樣,在被調用後,線程會釋放對象的鎖讓其他線程去競争,然後在定義的time到了之後自動被喚醒從阻塞狀态變為就緒狀态。

繼續閱讀