JUC併發程式設計快速入門篇(八)—— 阻塞佇列
阿新 • • 發佈:2021-12-30
BlockingQueue阻塞佇列
阻塞佇列,顧名思義,首先它是一個佇列, 通過一個共享的佇列,可以使得資料由佇列的一端輸入,從另外一端輸出;
- 當佇列是空的,從佇列中獲取元素的操作將會被阻塞
- 當佇列是滿的,從佇列中新增元素的操作將會被阻塞
- 試圖從空的佇列中獲取元素的執行緒將會被阻塞,直到其他執行緒往空的佇列插入新的元素
- 試圖向已滿的佇列中新增新元素的執行緒將會被阻塞,直到其他執行緒從佇列中移除一個或多個元素或者完全清空,使佇列變得空閒起來並後續新增
常用的佇列主要有以下兩種:
- 先進先出(FIFO):先插入的佇列的元素也最先出佇列,類似於排隊的功能。從某種程度上來說這種佇列也體現了一種公平性
- 後進先出(LIFO):後插入佇列的元素最先出佇列,這種佇列優先處理最近發生的事件(棧)
ArrayBlockingQueue(常用)
由陣列結構組成的有界阻塞佇列
BlockingQueue 核心方法
案例
public class BlockingQueueDemo { public static void main(String[] args) throws InterruptedException { //建立一個長度為3的阻塞佇列 BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3); // LinkedBlockingDeque<String> blockingDeque = new LinkedBlockingDeque<>(3); //新增第一組佇列 System.out.println(blockingQueue.add("a")); System.out.println(blockingQueue.add("b")); System.out.println(blockingQueue.add("c")); System.out.println(blockingQueue.element()); //System.out.println(blockingQueue.add("d"));//佇列大小為3,新增第4個元素丟擲異常 System.out.println(blockingQueue.remove());//a System.out.println(blockingQueue.remove());//b System.out.println(blockingQueue.remove());//c // System.out.println(blockingQueue.remove());//異常 //新增第二組佇列 System.out.println(blockingQueue.offer("a")); System.out.println(blockingQueue.offer("b")); System.out.println(blockingQueue.offer("c")); // System.out.println(blockingQueue.offer("d"));//佇列大小為3,新增第4個元素丟擲異常 System.out.println(blockingQueue.poll());//a System.out.println(blockingQueue.poll());//b System.out.println(blockingQueue.poll());//c // System.out.println(blockingQueue.poll());//異常 //第三組 blockingQueue.put("a"); blockingQueue.put("b"); blockingQueue.put("c"); // blockingQueue.put("d");//不會丟擲異常,自動處於執行緒等待阻塞狀態 System.out.println(blockingQueue.take()); System.out.println(blockingQueue.take()); System.out.println(blockingQueue.take()); // System.out.println(blockingQueue.take());//不會丟擲異常,當執行緒中沒有佇列的時候,無法取佇列,所以處於阻塞狀態 //第四組 System.out.println(blockingQueue.offer("a")); System.out.println(blockingQueue.offer("b")); System.out.println(blockingQueue.offer("c")); //設定阻塞時間,3L為時間長度,TimeUnit.SECONDS為時間單位。3S內未能進佇列,則自動結束 System.out.println(blockingQueue.offer("d",3L, TimeUnit.SECONDS)); } }
LinkedBlockingQueue(常用)
ArrayBlockingQueue 和 LinkedBlockingQueue 是兩個最普通也是最常用的阻塞佇列,一般情況下,在處理多執行緒間的生產者消費者問題,使用這兩個類足以。
由連結串列結構組成的有界(但大小預設值為integer.MAX_VALUE)阻塞佇列
public class BlockingQueueDemo { public static void main(String[] args) throws InterruptedException { //建立一個長度為3的阻塞佇列 BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3); // LinkedBlockingDeque<String> blockingDeque = new LinkedBlockingDeque<>(3); //新增第一組佇列 System.out.println(blockingQueue.add("a")); System.out.println(blockingQueue.add("b")); System.out.println(blockingQueue.add("c")); System.out.println(blockingQueue.element()); //System.out.println(blockingQueue.add("d"));//佇列大小為3,新增第4個元素丟擲異常 System.out.println(blockingQueue.remove());//a System.out.println(blockingQueue.remove());//b System.out.println(blockingQueue.remove());//c // System.out.println(blockingQueue.remove());//異常 //新增第二組佇列 System.out.println(blockingQueue.offer("a")); System.out.println(blockingQueue.offer("b")); System.out.println(blockingQueue.offer("c")); // System.out.println(blockingQueue.offer("d"));//佇列大小為3,新增第4個元素丟擲異常 System.out.println(blockingQueue.poll());//a System.out.println(blockingQueue.poll());//b System.out.println(blockingQueue.poll());//c // System.out.println(blockingQueue.poll());//異常 //第三組 blockingQueue.put("a"); blockingQueue.put("b"); blockingQueue.put("c"); // blockingQueue.put("d");//不會丟擲異常,自動處於執行緒等待阻塞狀態 System.out.println(blockingQueue.take()); System.out.println(blockingQueue.take()); System.out.println(blockingQueue.take()); // System.out.println(blockingQueue.take());//不會丟擲異常,當執行緒中沒有佇列的時候,無法取佇列,所以處於阻塞狀態 //第四組 System.out.println(blockingQueue.offer("a")); System.out.println(blockingQueue.offer("b")); System.out.println(blockingQueue.offer("c")); //設定阻塞時間,3L為時間長度,TimeUnit.SECONDS為時間單位。3S內未能進佇列,則自動結束 System.out.println(blockingQueue.offer("d",3L, TimeUnit.SECONDS)); } }
總結
1.在多執行緒領域:所謂阻塞,在某些情況下會掛起執行緒(即阻塞),一旦條件滿足,被掛起的執行緒又會自動被喚起
2.為什麼需要 BlockingQueue? 在 concurrent 包釋出以前,在多執行緒環境下,我們每個程式設計師都必須去自己控制這些細節,尤其還要兼顧效率和執行緒安全,而這會給我們的程式帶來不小的複雜度。使用後我們不需要關心什麼時候需要阻塞執行緒,什麼時候需要喚醒執行緒,因為這一切 BlockingQueue 都給你一手包辦了