本文通過經典的「生產者-消費者」模型,對比傳統 wait/notify 手動同步與 BlockingQueue 內置同步的實現差異,展示如何利用 JDK 並發包簡化多線程協作代碼。
使用 ReentrantLock 配合 Condition 實現緩衝區同步:
public class ConsumerProducer {
private static Buffer buffer = new Buffer();
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.execute(new ProducerTask());
executor.execute(new ConsumerTask());
executor.shutdown();
}
static class ProducerTask implements Runnable {
@Override
public void run() {
try {
int i = 1;
while (true) {
System.out.println("Producer writes " + i);
buffer.write(i++);
Thread.sleep((int)(Math.random() * 10000));
}
} catch (InterruptedException e) { e.printStackTrace(); }
}
}
static class ConsumerTask implements Runnable {
@Override
public void run() {
try {
while (true) {
System.out.println("\t\t\tConsumer reads " + buffer.read());
Thread.sleep((int)(Math.random() * 10000));
}
} catch (InterruptedException e) { e.printStackTrace(); }
}
}
static class Buffer {
private static final int CAPACITY = 1;
private final LinkedList<Integer> queue = new LinkedList<>();
private final Lock lock = new ReentrantLock();
private final Condition notEmpty = lock.newCondition();
private final Condition notFull = lock.newCondition();
public void write(int value) {
lock.lock();
try {
while (queue.size() == CAPACITY) {
System.out.println("Wait for notFull condition");
notFull.await();
}
queue.offer(value);
notEmpty.signal();
} catch (Exception e) { e.printStackTrace(); }
finally { lock.unlock(); }
}
public int read() {
int value = 0;
lock.lock();
try {
while (queue.isEmpty()) {
System.out.println("\t\t\tWait for notEmpty condition");
notEmpty.await();
}
value = queue.remove();
notFull.signal();
} catch (Exception e) { e.printStackTrace(); }
finally { lock.unlock(); }
return value;
}
}
}
ArrayBlockingQueue 是有界阻塞隊列,內部已實現線程安全的 put/take,無需手動編寫同步邏輯:
public class ConsumerProducerUsingBlockingQueue {
// 容量為 2 的有界隊列
private static ArrayBlockingQueue<Integer> buffer = new ArrayBlockingQueue<>(2);
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.execute(new ProducerTask());
executor.execute(new ConsumerTask());
executor.shutdown();
}
static class ProducerTask implements Runnable {
@Override
public void run() {
try {
int i = 1;
while (true) {
System.out.println("Producer writes " + i);
buffer.put(i++); // 隊列滿時自動阻塞
Thread.sleep((int)(Math.random() * 10000));
}
} catch (InterruptedException e) { e.printStackTrace(); }
}
}
static class ConsumerTask implements Runnable {
@Override
public void run() {
try {
while (true) {
System.out.println("\t\t\tConsumer reads " + buffer.take()); // 隊列空時自動阻塞
Thread.sleep((int)(Math.random() * 10000));
}
} catch (InterruptedException e) { e.printStackTrace(); }
}
}
}
| 隊列類型 | 底層結構 | 特性 | 適用場景 |
|---|---|---|---|
ArrayBlockingQueue |
數組 | 有界、FIFO、固定容量、支持公平鎖 | 固定緩衝區、生產消費速率平衡 |
LinkedBlockingQueue |
鏈表 | 可選有界(默認 Integer.MAX_VALUE)、吞吐量高 | 高併發任務隊列、線程池工作隊列 |
PriorityBlockingQueue |
堆 | 無界、按優先級排序、不允許 null | 優先級任務調度、定時任務隊列 |
| 操作 | 拋出異常 | 返回布爾值 | 阻塞等待 | 超時等待 |
|---|---|---|---|---|
| 插入 | add(e) |
offer(e) |
put(e) |
offer(e, time, unit) |
| 移除 | remove() |
poll() |
take() |
poll(time, unit) |
| 檢查 | element() |
peek() |
— | — |
LinkedBlockingQueue,需優先級用 PriorityBlockingQueue,固定緩衝用 ArrayBlockingQueue。