[英]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()
:
isFull
為false
isFull
設置為true
然后,其余線程中的一個(或多個)可以調用taxi()
。 如果這樣做,他們:
isFull
為false
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.