👋hi,我不是一名外包公司的員工,也不會偷吃茶水間的零食,我的夢想是能寫高端CRUD
🔥 2025本人正在沉淀中… 博客更新速度++
👍 歡迎點贊、收藏、關注,跟上我的更新節奏
📚歡迎訂閱專欄,專欄名《在2B工作中尋求并發是否搞錯了什么》
前言
你是否在線程池工具類里看到過它的身影?
public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
你是否會好奇LinkedBlockingQueue是啥呢?
沒有關系,小手手點上關注,跟上主播的節奏。
什么是LinkedBlockingQueue?
LinkedBlockingQueue 是一個基于鏈表的線程安全阻塞隊列,常用于生產者-消費者模式。
數據結構:
- 基于單向鏈表實現,隊列頭尾分別通過 head 和 last 指針維護。
- 默認容量為 Integer.MAX_VALUE(近似無界隊列),但可手動指定固定容量。
線程安全
- 使用兩把鎖分離設計(入隊鎖 putLock 和出隊鎖 takeLock),提高并發性能。
- 通過 ReentrantLock 和 Condition 實現阻塞(隊列空時阻塞消費者,隊列滿時阻塞生產者)。
阻塞操作
- put():隊列滿時阻塞生產者線程。
- take():隊列空時阻塞消費者線程。
- 非阻塞方法:offer()(失敗返回 false)、poll()(失敗返回 null)
簡單說說,和我們之前說的ArrayBlokcingQueue的區別:
特性 | LinkedBlockingQueue | ArrayBlockingQueue |
---|---|---|
底層結構 | 鏈表 | 數組 |
默認容量 | Integer.MAX_VALUE(無界) | 必須顯式指定 |
鎖機制 | 雙鎖分離(更高并發) | 單鎖控制 |
內存占用 | 動態擴展(鏈表節點開銷) | 預分配連續內存 |
簡單使用LinkedBlockingQueue
因為我們的LinkedBlockingQueue也是實現了BlockingQueue的接口,所以下面的代碼例子,會有這些方法。
構造方法
首先從構造方法說起吧,LinkedBlockingQueue有3個構造方法:
// 沒有傳任何參數,默認容量大小為Integer.MAX_VALUE
public LinkedBlockingQueue();// 容量大小為入參
public LinkedBlockingQueue(int capacity)// 容量大小為Integer.MAX_VALUE,集合中初始化元素為c
public LinkedBlockingQueue(Collection<? extends E> c)
添加元素入隊操作
add(E e)方法:簡單粗暴,非阻塞添加元素,隊列滿了的話直接拋IllegalStateException
異常。
public static void main(String[] args) {BlockingQueue<Object> queue = new LinkedBlockingQueue<>(1);queue.add("A");queue.add("B"); // Exception in thread "main" java.lang.IllegalStateException: Queue full
}
offer(E e)方法:非阻塞添加元素,成功返回boolean,添加成功為true,添加失敗為false
public static void main(String[] args) {BlockingQueue<Object> queue = new LinkedBlockingQueue<>(1);boolean result = queue.offer("A"); // trueSystem.out.println(result);result = queue.offer("B"); // falseSystem.out.println(result);
}
offer(E e, long timeout, TimeUnit unit)方法: 向隊列添加元素。如果隊列滿了,就阻塞線程,等待一段時間,一段時間過后隊列還是滿的話,意味添加元素失敗返回false,否則返回true。
public static void main(String[] args) throws InterruptedException {BlockingQueue<Object> queue = new LinkedBlockingQueue<>(1);boolean result = queue.offer("A", 1, TimeUnit.SECONDS); // trueSystem.out.println(result);result = queue.offer("B", 1, TimeUnit.SECONDS); // falseSystem.out.println(result);
}
但如果我們這樣等待一會的話,執行結果就會不一樣:
public static void main(String[] args) throws InterruptedException {BlockingQueue<Object> queue = new LinkedBlockingQueue<>(1);boolean result = queue.offer("A", 1, TimeUnit.SECONDS); // trueSystem.out.println(result);// 啟動另一個生產者線程new Thread(() -> {Boolean res;try {res = queue.offer("B", 2, TimeUnit.SECONDS); // 隊列滿了,阻塞2s等待} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(res); // true}).start();// 移除隊列中的元素queue.poll();
}
put(E e)方法: 阻塞線程,直到隊列不為空或中斷者線程。
public static void main(String[] args) throws InterruptedException {BlockingQueue<Object> queue = new LinkedBlockingQueue<>(1);queue.put("A");queue.put("B"); // 隊列已滿,線程阻塞在這
}
移除元素出隊操作
remove()方法:如果隊列為空,直接拋出NoSuchElementException
異常。
public static void main(String[] args) {BlockingQueue<Object> queue = new LinkedBlockingQueue<>(1);queue.remove(); // Exception in thread "main" java.util.NoSuchElementException
}
poll()方法:非阻塞獲取隊列頭元素,如果隊列為空,直接返回null。
public static void main(String[] args) {BlockingQueue<String> queue = new LinkedBlockingQueue<>(1);String poll = queue.poll();System.out.println(poll); // null(隊列中沒有元素)queue.offer("A"); // 向隊列中添加元素poll= queue.poll();System.out.println(poll); // A
}
poll(long timeout, TimeUnit unit)方法:阻塞一段時間獲取隊列中的元素,如果超過時間了,就隊列還是為空,就返回null。
public static void main(String[] args) throws InterruptedException {BlockingQueue<String> queue = new LinkedBlockingQueue<>(1);// 創建一個線程,3s后生產1個元素到隊列中new Thread(() -> {try {Thread.sleep(2000);queue.offer("A");} catch (InterruptedException e) {e.printStackTrace();}}).start();// 消費者,阻塞5s獲取元素String poll= queue.poll(5 , TimeUnit.SECONDS);System.out.println(poll); // A
}
take()方法:阻塞線程獲取隊列中的元素,直到隊列不為空,或者被其他線程中斷,拋出異常停止。
public static void main(String[] args) throws InterruptedException {BlockingQueue<String> queue = new LinkedBlockingQueue<>(1);// 創建一個消費者線程,獲取隊列中的元素Thread consumerThread = new Thread(() -> {try {String take = queue.take(); // 在這個案例中,線程會被一直阻塞到這,直到被中斷System.out.println("消費者線程獲取到元素:" + take);} catch (InterruptedException e) {System.out.println("線程者線程被中斷了");}});consumerThread.start();// 隔3s后,中斷消費者線程TimeUnit.SECONDS.sleep(1);consumerThread.interrupt();
}輸出結果:
線程者線程被中斷了
檢查隊列中的元素
element()
方法: 返回隊列頭的元素,但是如果隊列為空,element
方法會拋出NoSuchElementException
異常。
public static void main(String[] args) throws InterruptedException {BlockingQueue<String> queue = new LinkedBlockingQueue<>(2);queue.offer("A");queue.offer("B");System.out.println(queue.element()); // Aqueue.poll(); // 頭元素出隊System.out.println(queue.element()); // B
}// 異常的情況
public static void main(String[] args) throws InterruptedException {BlockingQueue<String> queue = new LinkedBlockingQueue<>(2);String element = queue.element(); // Exception in thread "main" java.util.NoSuchElementException
}
peek()
方法:和element()
方法差不多,返回隊列頭的元素,如果隊列為空,會返回null
。
public static void main(String[] args) throws InterruptedException {BlockingQueue<String> queue = new LinkedBlockingQueue<>(2);String element = queue.peek();System.out.println(element); // null
}
后話
這就結束了?沒有的,寶貝,沒有的。
這里只是簡答的使用,小手手點上關注,主播下一篇,直接開始看LinkedBlockingQueue源碼。