“嗨,阿米戈!”
“我想和你深入探討 wait-notify。wait-notify 方法為線程交互提供了一種方便的機制。它們還可以用於構建複雜的線程交互高級機制。”
“我將從一個小例子開始。假設我們有一個服務器程序,必須執行用戶通過網站創建的各種任務。用戶可以在不同時間添加各種任務。任務是資源密集型的,但我們服務器的 8 -核心處理器可以應付。我們應該如何在服務器上執行任務?”
“首先,我們將創建一組工作線程,與處理器內核的數量一樣多。每個線程都能夠在自己的內核上運行:線程不會相互干擾,處理器內核也不會坐以待斃。”
“其次,我們將創建一個隊列對象,其中將添加用戶的任務。不同類型的任務將對應不同的對象,但它們都將實現 Runnable 接口,以便它們可以運行。”
“你能給我一個任務對象的例子嗎?”
“一探究竟:”
class Factorial implements Runnable
{
public int n = 0;
public long result = 1;
public Factorial (int n)
{
this.n = n;
}
public void run()
{
for (int i = 2; i <= n; i++)
result *= i;
}
}
“到目前為止,一切都很好。”
“太好了。那麼讓我們檢查一下隊列對象應該是什麼樣子。你能告訴我什麼嗎?”
“它必須是線程安全的。它由一個從用戶那裡接收任務對象的線程加載任務對象,然後任務由工作線程接收。”
“是的。如果我們暫時沒有任務怎麼辦?”
“那麼工作線程應該等到有更多的時候。”
“沒錯。現在想像一下,所有這些都可以構建在一個隊列中。檢查一下:”
public class JobQueue
{
ArrayList jobs = new ArrayList();
public synchronized void put(Runnable job)
{
jobs.add(job);
this.notifyAll();
}
public synchronized Runnable getJob()
{
while (jobs.size() == 0)
this.wait();
return jobs.remove(0);
}
}
“我們有一個getJob方法來檢查任務列表是否為空。線程然後進入休眠狀態(等待),直到列表中出現某些內容。”
“還有put方法,它可以讓你向列表中添加一個新任務。一旦添加了一個新任務,就會調用notifyAll方法。調用這個方法會喚醒所有在 getJob 方法中休眠的工作線程。”
“你能記起 wait 和 notify 方法是如何工作的嗎?”
“wait 方法僅在同步塊內調用,在互斥對像上。在我們的例子中:this。此外,還會發生兩件事:
1)線程睡著了。
2)線程暫時釋放互斥量(直到被喚醒)。
“在那之後,其他線程可以進入同步塊並獲取相同的互斥量。”
“ notifyAll方法也只能在互斥對象的同步塊內調用。在我們的例子中:這個。此外,還會發生兩件事:”
1)所有等待這個互斥對象的線程都被喚醒。
2) 一旦當前線程退出同步塊,其中一個被喚醒的線程獲取互斥量並繼續其工作。當它釋放互斥鎖時,另一個被喚醒的線程獲取互斥鎖,等等。
“這很像一輛公共汽車。你進去想付錢,但沒有司機。所以你“睡著了”。最後,公共汽車擠滿了人,但仍然沒有人付錢。然後司機到達並說,«Fare, please»。這是……的開始。
“有趣的比較。但什麼是公共汽車?”
“胡里奧對此進行了解釋。21 世紀使用了這些奇怪的東西。”
GO TO FULL VERSION