JavaRush /Java Blog /Random-TW /Java中的SynchronousQueue範例-解決Producer Consumer問題
profeg
等級 18

Java中的SynchronousQueue範例-解決Producer Consumer問題

在 Random-TW 群組發布
Java中的SynchronousQueue範例-解決Producer Consumer問題
SynchronousQueue是一種特殊類型的BlockingQueue,其中每個插入操作都必須等待另一個執行緒中對應的刪除命令,反之亦然。當您在 SynchronousQueue 上呼叫put() 方法時,它會阻塞,直到另一個執行緒從中取得該元素。因此,如果另一個執行緒嘗試從中刪除一個元素,但該元素不存在,則該執行緒會阻塞,直到另一個執行緒將該元素放入佇列中。你可以把 SynchronousQueue 想像成一個拿著奧運火炬奔跑的運動員(線程),他帶著火炬(正在傳遞的對象)奔跑,並將其傳遞給另一邊等待的另一位運動員。如果你留意這個名字,你就會明白 SynchronousQueue 如此命名是有原因的;它同步地將資料傳輸到另一個執行緒;它等待某人獲取數據,而不是僅僅將其放入並退出(非同步操作)。如果您熟悉 CSP 和 Ada,那麼您就會知道同步佇列類似於執行緒的會議。它們非常適合控制傳輸結構,其中一個執行緒中運行的物件必須與另一個執行緒中的物件同步,以便向其傳遞一些資訊、事件或任務。在先前的多執行緒程式設計教學中,我們學習如何使用wait和notify以及BlockingQueue方法來解決生產者-消費者問題。現在我們將學習如何使用 SynchronousQueue 應用生產者-消費者模式。此類還支援對生產者線程和消費者線程的等待進行排序的公平行為。預設情況下,不保證此順序。但是,使用公平屬性建立的佇列可保證 FIFO (先進先出)佇列 中執行緒的存取。
Java 中使用 SynchronousQueue 的生產者/消費者。
Java中的SynchronousQueue範例 - 解決Producer Consumer問題 - 1正如我上面所說,對於理解任何程式語言中的線程間通訊 來說,沒有什麼比生產者-消費者問題更好的了。在此問題中,一個執行緒充當生成事件和任務的生產者,另一個執行緒充當其消費者。共享緩衝區用於將資料從生產者傳輸到消費者。在極端情況下解決這個問題會很困難,例如,當製造商被迫等待時,因為... 緩衝區已滿或消費者被迫等待,因為 緩衝區為空。這個問題很容易解決,因為... 阻塞佇列不僅提供了儲存資料的緩衝區,還提供了流量控制,如果緩衝區已滿,則阻塞呼叫put() 方法的執行緒(Producer),如果緩衝區已滿,則阻塞呼叫take() 方法的執行緒(Consumer)。緩衝區為空。現在我們將使用 SynchronousQueue 來解決相同的問題,SynchronousQueue 是一種特殊的零容量並行集合。在下面的範例中,我們有兩個線程,分別稱為PRODUCERCONSUMER(總是為線程命名,這是一種非常好的多線程程式設計風格)。第一個線程發布遊戲中的分數,第二個線程使用它。遊戲中的分數無非就是一個String類型的物件。但是如果您使用不同的類型運行該程序,您將不會注意到任何差異。要了解 SynchronousQueue 是如何運作的,以及如何解決生產者-消費者問題,您需要:要么在 Eclipse 環境中運行程式進行調試(debug),要么乾脆通過註釋掉consumer.start()來啟動生產者線程;如果消費者線程沒有運行,那麼生產者線程將被阻塞在queue.put(event); 如果運行,您將無法看到生產者 [PRODUCER] 發布 :FOUR 事件。發生這種情況是因為 SynchronousQueue 的特定行為,它確保發布資料的執行緒將阻塞,直到另一個執行緒取得數據,反之亦然。您可以透過註解掉 Producer.start(); 來測試其餘程式碼。並僅啟動消費者線程。 如果仔細研究程式的輸出內容,您會發現輸出的順序是相反的。看起來[CONSUMER]線程在[PRODUCER]線程產生資料 之前就已經取得了資料。這是因為 SynchronousQueue 預設不保證排隊。但它具有公平規則,以 FIFO 順序設定對執行緒的存取。您可以透過將 true 傳遞給重載的SynchronousQueue 建構函式來啟用這些規則,如下所示: import java.util.concurrent.SynchronousQueue; /** * Java Program to solve Producer Consumer problem using SynchronousQueue. A * call to put() will block until there is a corresponding thread to take() that * element. * * @author Javin Paul */ public class SynchronousQueueDemo{ public static void main(String args[]) { final SynchronousQueue queue = new SynchronousQueue (); Thread producer = new Thread("PRODUCER") { public void run() { String event = "FOUR"; try { queue.put(event); // thread will block here System.out.printf("[%s] published event : %s %n", Thread .currentThread() .getName(), event); } catch (InterruptedException e) { e.printStackTrace(); } } }; producer.start(); // starting publisher thread Thread consumer = new Thread("CONSUMER") { public void run() { try { String event = queue.take(); // thread will block here System.out.printf("[%s] consumed event : %s %n", Thread .currentThread() .getName(), event); } catch (InterruptedException e) { e.printStackTrace(); } } }; consumer.start(); // starting consumer thread } } Output: [CONSUMER] consumed event : FOUR [PRODUCER] published event : FOUR new SynchronousQueue(boolean fair).
關於 Java 中的 SynchronousQueue,您需要記住什麼。

以下是 Java 中這種特殊類型的阻塞佇列的一些重要屬性。以同步方式將資料從一個線程傳遞到另一個線程非常有用。該隊列沒有容量並且被阻塞,直到另一個執行緒釋放它。

  1. SynchronousQueue 會阻塞,直到一個執行緒準備好取得資料為止,另一個執行緒將嘗試放入資料。
  2. SynchronousQueue 沒有磁碟區。也就是說,它不包含數據。
  3. SynchronousQueue 用於實現前向排隊策略,其中線程將控制權傳遞給等待線程,或在允許的情況下建立新線程,否則不轉移控制權。
  4. 該隊列不允許空資料。嘗試新增 null 元素將會拋出NullPointerException
  5. 如果您使用 Collection 中的其他方法(例如 contains),SynchronousQueue 的行為就像一個空集合。
  6. 您不能使用 SynchronousQueue 的 peek 方法,因為該元素僅在您嘗試刪除它時才存在;此外,在另一個執行緒嘗試刪除元素之前,您將無法插入元素(使用任何方法)。
  7. 您將無法對 SynchronousQueue 使用迭代器,因為... 它沒有元素。
  8. SynchronousQueue 可以使用公平規則創建,其中保證按 FIFO 順序存取執行緒。
也許這就是Java中SynchronousQueue的全部我們研究了這個多執行緒集合的一些特殊功能,並學習如何使用 Java 中的 SynchronousQueue 來解決經典的生產者-消費者問題。順便說一句,稱其為隊列並不完全正確,因為... 它不包含元素。在另一個執行緒呼叫 take() 之前,對 put() 的呼叫不會完成。更正確的做法是將其視為線程的聚會場所,線程在此共享一個物件。換句話說,它是 Java 中物件同步傳遞的實用程序,也許是wait 和 notification方法的更安全的替代方案。
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION