1. 定義
要點
-
與Java併發(二十二)----同步模式之保護性暫停中的保護性暫停中的 GuardObject 不同,不需要產生結果和消費結果的線程一一對應
-
這樣的好處是消費隊列可以用來平衡生產和消費的線程資源
-
生產者僅負責產生結果數據,不關心數據該如何處理,而消費者專心處理結果數據
-
消息隊列是有容量限制的,滿時不會再加入數據,空時不會再消耗數據
-
JDK 中各種阻塞隊列,採用的就是這種模式
2. 實現
// 線程安全,只有get方法,且沒有子類
final class Message {
private int id;
private Object message;
public Message(int id, Object message) {
this.id = id;
this.message = message;
}
public int getId() {
return id;
}
public Object getMessage() {
return message;
}
}
// 消息隊列類 Java 線程之間通信
class MessageQueue {
private LinkedList<Message> queue;
private int capacity;
public MessageQueue(int capacity) {
this.capacity = capacity;
queue = new LinkedList<>();
}
public Message take() {
synchronized (queue) {
while (queue.isEmpty()) {
log.debug("沒貨了, wait");
try {
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 從隊列頭部獲取消息並返回
Message message = queue.removeFirst();
queue.notifyAll();
return message;
}
}
public void put(Message message) {
synchronized (queue) {
while (queue.size() == capacity) {
log.debug("庫存已達上限, wait");
try {
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 將新的消息加在尾部
queue.addLast(message);
queue.notifyAll();
}
}
}
3. 應用
MessageQueue messageQueue = new MessageQueue(2);
// 4 個生產者線程, 下載任務
for (int i = 0; i < 4; i++) {
int id = i;
new Thread(() -> {
try {
log.debug("download...");
List<String> response = Downloader.download(); // 模擬生產消息,三秒後返回response
log.debug("try put message({})", id);
messageQueue.put(new Message(id, response));
} catch (IOException e) {
e.printStackTrace();
}
}, "生產者" + i).start();
}
// 1 個消費者線程, 處理結果
new Thread(() -> {
while (true) {
Message message = messageQueue.take();
List<String> response = (List<String>) message.getMessage();
log.debug("take message({}): [{}] lines", message.getId(), response.size());
}
}, "消費者").start();
某次運行結果
10:48:38.070 [生產者3] c.TestProducerConsumer - download...
10:48:38.070 [生產者0] c.TestProducerConsumer - download...
10:48:38.070 [消費者] c.MessageQueue - 沒貨了, wait
10:48:38.070 [生產者1] c.TestProducerConsumer - download...
10:48:38.070 [生產者2] c.TestProducerConsumer - download...
10:48:41.236 [生產者1] c.TestProducerConsumer - try put message(1)
10:48:41.237 [生產者2] c.TestProducerConsumer - try put message(2)
10:48:41.236 [生產者0] c.TestProducerConsumer - try put message(0)
10:48:41.237 [生產者3] c.TestProducerConsumer - try put message(3)
10:48:41.239 [生產者2] c.MessageQueue - 庫存已達上限, wait
10:48:41.240 [生產者1] c.MessageQueue - 庫存已達上限, wait
10:48:41.240 [消費者] c.TestProducerConsumer - take message(0): [3] lines
10:48:41.240 [生產者2] c.MessageQueue - 庫存已達上限, wait
10:48:41.240 [消費者] c.TestProducerConsumer - take message(3): [3] lines
10:48:41.240 [消費者] c.TestProducerConsumer - take message(1): [3] lines
10:48:41.240 [消費者] c.TestProducerConsumer - take message(2): [3] lines
10:48:41.240 [消費者] c.MessageQueue - 沒貨了, wait