線程調用notifyAll喚醒其他線程之後

寫在前面

今天做老師給的多線程的作業,熟悉線程之間通信的方法,像wait,notify,notifyAll,通過調試代碼後,產生了很多疑問,我都紛紛作了猜測,唯有這個疑問我還沒搞清楚,網上也難以翻到:線程調用notifyAll喚醒其他線程後其自身的狀態是怎樣的?我給的猜想是進入就緒隊列中。

尋找答案

通過對下面代碼的調試,理清楚線程的走向。

public class lab3_3 {
    public static void main(String[] args) {
        Object lock = new Object();
        MyThread3_3 t1 = new MyThread3_3(0, 'A', lock);
        MyThread3_3 t2 = new MyThread3_3(1, 'B', lock);
        MyThread3_3 t3 = new MyThread3_3(2, 'C', lock);
        t1.start();
        t2.start();
        t3.start();
    }
}

class MyThread3_3 extends Thread {
    Object lock;
    char c;
    int flag;
    static int current = 0; //意味着先輸出A字符

    public MyThread3_3(int flag, char c, Object lock) {
        this.flag = flag;
        this.lock = lock;
        this.c = c;
    }

    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
*           synchronized (lock) {
                while (flag != current) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(c);
                current = ++current % 3;
                lock.notifyAll();
            }
        }
    }
}

上面代碼是控制順序輸出“ABCABCABC”。首先我在*處設下斷點調試,按順序先後讓後兩個線程②③獲得鎖,由於第一個字符A應該由線程①來獲得鎖並輸出,所以此時線程②③會直接調用wait方法進入等待隊列,如下圖:

image-20200414221137081

然後線程①獲得鎖,執行代碼塊,輸出字符,最後調用notifyAll方法喚醒其他線程,還沒走完代碼塊,此時狀態如下圖,線程②③都爲MONITOR,在這裏我猜測該狀態是線程就緒狀態,沒有確切的證據,因爲我網上翻不到。

image-20200414221433114

接着走過代碼塊,來到for循環的判斷語句時,狀態開始變了,如下圖:

image-20200414221728074

只有線程③進入RUNNING狀態,線程②還是就緒狀態,這裏之前我又嘗試了不同順序的調用wait方法,比如一開始是線程②③的順序調用wait方法,我又換了③②的順序調用wait方法,發現上面的結果真的不一樣,喚醒後變成了最後一個線程③在就緒隊列中。明明是先調用wait方法先進入等待隊列的,爲什麼不是最先從就緒隊列出來的?網上有找到說就緒隊列是隊列形式但實際上採用線程池的形式,CPU調度不一定按先進先出的順序調度。不過我又多給出三個線程來調試,發現都是最後調用wait的方法,就第一個從就緒隊列進到RUNNING狀態,不由得讓我做出猜測:線程以堆棧的形式進入等待隊列,然後出棧進入就緒隊列,就緒隊列是隊列的數據結構,所以最後進入等待隊列的線程,優先從就緒隊列中出來。當然,確切的證據還是沒有。

接着上面的步驟,直接讓線程①獲得鎖執行代碼塊,發現線程它的狀態變爲MONITOR(就緒)了,沒法再直接調用,此時也只能調用線程③了,由於當前是字符B的輸出,也就是應該線程②獲得鎖,所以線程③又調用wait方法進入等待隊列中,此時線程②從就緒隊列中出來了,變爲RUNNING狀態。這個步驟我又嘗試了多次其他不同的順序,於是又給出猜測,也是適合這次內容主題的猜測:線程①調用notifyAll方法喚醒其他線程後,自身無法再直接獲得該鎖,進入了就緒隊列,而且是隊列的末尾,符合了隊列的特徵。

寫在最後

我只是給出多個猜測,可能是正確的也說不定哈哈🤭我就暫且這樣想吧,畢竟多線程還有好多內容呢,而且不記下來恐怕日後會煙消雲散。能力之外的深入探究,就待我歸來之時再談吧。

天色已晚,就寫到這裏。

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