簡體   English   中英

為什么在此示例中,notifyAll()不喚醒所有線程?

[英]Why is notifyAll() not waking all of the threads in this example?

我試圖弄清楚如何使用wait&notify,所以我寫了這個小例子,其中有幾架飛機在起飛前等待跑道清除,我遇到的問題是飛機何時起飛,並調用notifyAll(),似乎只有一個線程被喚醒,即我希望所有線程都報告它們已被通知,但仍在等待。 實際發生的情況是只有一個線程被喚醒,其余線程什么也不做。 為什么似乎只有一個線程被喚醒,我該如何解決?

class Plane extends Thread
{
    Runway runway;

    Plane(int id, Runway runway)
    {
        super(id + "");
        this.runway = runway;

    }

    public void run()
    {
        runway.taxi();
        runway.takeoff();
    }
}

class Runway
{
    boolean isFull;

    Runway()
    {
        isFull = false;;
    }

    public synchronized void taxi()
    {
        System.out.println(Thread.currentThread().getName() + " started to taxi");
        while(isFull)
        {
            System.out.println(Thread.currentThread().getName() + " is queued");
            try
            {
                wait();
            }
            catch(InterruptedException e){}     
        }
        isFull = true;
        System.out.println(Thread.currentThread().getName() + " entering runway");
    }

    public synchronized void takeoff()
    {
        try
        {
            Thread.currentThread().sleep(1000);
        }
        catch(InterruptedException e){}
        System.out.println(Thread.currentThread().getName() + " took off");
        isFull = false;
        notifyAll();
    }

    public static void main(String[] args)
    {
        Runway runway = new Runway();
        new Plane(1, runway).start();
        new Plane(2, runway).start();
        new Plane(3, runway).start();
        new Plane(4, runway).start();
    }
}

感謝您抽出寶貴的時間來幫助我:)

因為notifyAll()不是wakeAll()。 所有線程都被通知,但是只有一個線程持有該密鑰並正在運行。 所有其他人再次等待拉。

這就是它的作用。 它“通知”所有等待的線程,但是只有一個喚醒並獲取CPU。 notify()根據基礎線程實現選擇的內容選擇一個等待線程。 notifyAll()給所有等待線程平等的競爭機會。 但是無論哪種方式,只有一個線程采用上下文。

假設您有4個平面,每個平面都是start()一個接一個。

所有這4個人將嘗試先致電taxi()然后再takeoff()

第一個將調用taxi()

  • 獲取鎖,
  • 發現isFullfalse
  • isFull設置為true
  • 返回,釋放鎖

然后,其余線程中的一個(或多個)可以調用taxi() 如果這樣做,他們:

  • 獲取鎖
  • 發現isFullfalse
  • 調用wait()釋放鎖

要么

  • 嘗試獲取鎖時阻塞

同時,從taxi()返回的線程將調用takeoff() 這將:

  • 獲取鎖
  • 睡一秒鍾
  • 通知任何正在等待的線程
  • 返回,釋放鎖。

那么,這如何解釋您所看到的呢?

假設當第一個線程從taxi()返回時,它立即能夠重新獲取鎖定並啟動takeoff()調用。 然后它將在保持鎖定的同時調用sleep() 這樣可以防止其他任何線程啟動其taxi()調用(如果尚未啟動)。 然后在睡眠之后,它將調用notifyAll() 但這只會通知進入taxi()調用和已調用wait()的線程。 在開始進行taxi()調用時被阻塞的任何線程將永遠不會看到通知。

(對於不在wait()調用中的線程,通知永遠不會排隊。)

這可能嗎? 好吧,是的。

啟動線程是一個相對昂貴/耗時的過程,並且第一個線程很有可能在下一個線程啟動之前就要做很多工作。 很有可能在第二個嘗試調用taxi()之前,它將一直進入sleep調用。

其余線程可能重復相同的模式。 當進入taxi()每個線程都可能釋放后,在安排另一個線程之前重新獲取它。 (線程調度是由操作系統處理的,它是為效率而不是公平性進行優化。如果要公平調度,則需要使用Lock對象。)


...如何解決?

更改代碼,以使您在持有鎖時不會sleep 例如:

public void takeoff() {
    try {
        Thread.currentThread().sleep(1000);
    } catch (InterruptedException e) {
        // squash ...
    }
    System.out.println(Thread.currentThread().getName() + " took off");
    synchronize (this) {
        isFull = false;
        notifyAll();
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM