這

“嗨,阿米戈!”

“我想和你深入探討 wait-notify。wait-notify 方法為線程交互提供了一種方便的機制。它們還可以用於構建複雜的線程交互高級機制。”

“我將從一個小例子開始。假設我們有一個服務器程序,必須執行用戶通過網站創建的各種任務。用戶可以在不同時間添加各種任務。任務是資源密集型的,但我們服務器的 8 -核心處理器可以應付。我們應該如何在服務器上執行任務?”

“首先,我們將創建一組工作線程,與處理器內核的數量一樣多。每個線程都能夠在自己的內核上運行:線程不會相互干擾,處理器內核也不會坐以待斃。”

“其次,我們將創建一個隊列對象,其中將添加用戶的任務。不同類型的任務將對應不同的對象,但它們都將實現 Runnable 接口,以便它們可以運行。”

“你能給我一個任務對象的例子嗎?”

“一探究竟:”

調用run()方法時計算n階乘的類
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 世紀使用了這些奇怪的東西。”