| 操作 | 拋出異常 | 返回特殊值 | 阻塞等待 | 超時等待 |
| 插入 | add(e) | offer(e) | put(e) | offer(e, time, unit) |
| 移除 | remove() | poll() | take() | poll(time, unit) |
| 檢查 | element() | peek() | 不可用 | 不可用 |
最佳實踐:優先使用 offer/poll/peek 非阻塞版本,通過返回值判斷成敗;阻塞版本用於明確需要等待的業務邏輯。
原理剖析:以 ArrayBlockingQueue 為例
public class ArrayBlockingQueue extends AbstractQueue implements BlockingQueue, Serializable {
final Object[] items; // 環形數組
int putIndex; // 寫索引
int takeIndex; // 讀索引
int count; // 元素數量
final ReentrantLock lock; // 獨佔鎖
private final Condition notEmpty; // 非空條件
private final Condition notFull; // 非滿條件
public void put(E e) throws InterruptedException {
lock.lockInterruptibly();
try {
while (count == items.length) notFull.await(); // 滿則等待
items[putIndex] = e;
putIndex = (putIndex + 1) % items.length;
count++;
notEmpty.signal(); // 喚醒消費者
} finally { lock.unlock(); }
}
public E take() throws InterruptedException {
lock.lockInterruptibly();
try {
while (count == 0) notEmpty.await(); // 空則等待
E e = items[takeIndex];
items[takeIndex] = null;
takeIndex = (takeIndex + 1) % items.length;
count--;
notFull.signal(); // 喚醒生產者
return e;
} finally { lock.unlock(); }
}
}
經典應用場景代碼
1. 兩線程互斥等待(交替打印)
BlockingQueue q = new ArrayBlockingQueue<>(3);
// 生產者
new Thread(() -> { while(true){ q.put(1); System.out.println("放入"); }}).start();
// 消費者
new Thread(() -> { while(true){ q.take(); System.out.println("取出"); }}).start();
2. 主從線程交替執行(替代 wait/notify)
class Business {
BlockingQueue q1 = new ArrayBlockingQueue<>(1);
BlockingQueue q2 = new ArrayBlockingQueue<>(1);
{ q2.put(1); } // 初始阻塞 q2
void main(int i) throws InterruptedException {
q1.put(1); // 阻塞自己
for(int j=0;j<100;j++) System.out.println("main "+j);
q2.take(); // 喚醒子線程
}
void sub(int i) throws InterruptedException {
q2.put(1);
for(int j=0;j<10;j++) System.out.println("sub "+j);
q1.take();
}
}
注意:BlockingQueue 內部已同步,切勿在方法上再加 synchronized,否則死鎖。
3. 標準生產者-消費者模板(API 文檔範例)
class Producer implements Runnable {
private final BlockingQueue q;
Producer(BlockingQueue q){ this.q=q; }
public void run(){ try{ while(true) q.put(produce()); }catch(InterruptedException e){} }
Object produce(){ return new Object(); }
}
class Consumer implements Runnable {
private final BlockingQueue q;
Consumer(BlockingQueue q){ this.q=q; }
public void run(){ try{ while(true) consume(q.take()); }catch(InterruptedException e){} }
void consume(Object x){}
}
class Setup {
void main(){
BlockingQueue q = new LinkedBlockingQueue();
new Thread(new Producer(q)).start();
new Thread(new Consumer(q)).start();
new Thread(new Consumer(q)).start();
}
}
實戰避坑指南
- 無界隊列(LinkedBlockingQueue 預設)可能導致 OOM,生產環境必須指定容量
- PriorityBlockingQueue 無界且不阻塞生產者,消費者需處理積壓風險
- DelayQueue 僅消費者阻塞,生產者極快,注意內存壓力
- SynchronousQueue 適合線程池拒絕策略或直接傳遞場景
- 併發度極高時考慮
ConcurrentLinkedQueue(非阻塞)+ 自定義信號量
- 上一篇文章: WSDL 文檔結構完整詳解:核心元素、類型定義、綁定與服務端點解析
- 下一篇文章: Java 線程兩種創建方式與交替執行實戰:Runnable vs Thread、wait/notify 同步控制詳解
Copyright ©
程式碼之家 All Rights Reserved