生產者消費者模式,以及基於BlockingQueue的快速實現

王若伊_恩赐解脱發表於2024-08-27

什麼是生產者消費者模式?
簡單來說就是有兩個角色,一個角色主要負責生產資料,一個角色主要負責消費(使用)資料。
那麼生產者直接依賴消費者,然後直接呼叫是否可以?
答案是可以的,但是有些場景無法及時解決,典型的就是生產者消費者的速度無法同步,導致整體的速度上不去的情況。執行速度永遠取決於二者的最小速度(假設生產者和消費者的速度時快時慢)。
一般的解決方案是什麼呢?
我們通常會加一箇中介軟體,作為緩衝佇列。

生產者生產好的資料丟到緩衝佇列中,消費者根據自身情況及時獲取資料,處理資料,如下圖:

典型的實現就是Mq,比如Rocket mq、rabbit mq等,這是微服務、分散式場景下一個常見的中介軟體。

在服務內部,我們通常也會出現生產者消費者的模式,這時候引入mq顯然有一些過重,我們需要更高效,更簡潔的辦法。
最簡單的辦法就是用一個queue,或者用一個List,生產者向隊尾增加資料,消費者從隊頭取資料。
但是這樣做首先會存在多執行緒共享資料的場景,需要做一些鎖來解決執行緒同步的問題。
其次消費者在搶佔資料時,我們又希望可以區分公平搶佔和非公平搶佔鎖,
同時我們還要考慮緩衝佇列是否要有界,如果無界的話,資源是否會存在耗盡問題,
而且我們還要考慮是否可以更優雅的平衡生產者和消費者的處理速度,比如這樣:
如果緩衝佇列滿了,那麼生產者執行緒自己會掛起停止生產,當佇列有空餘空間時(注意不是全部清空資料),生產者會被喚醒繼續生產;
如果緩衝佇列空了,那麼消費者執行緒自己會掛起停止消費,當佇列有資料時(注意不是全部堆滿資料),消費者會被喚醒繼續消費。
諸如此類等等問題,如果我們想開發這樣一個緩衝佇列,要考慮和解決還是比較有難度的。
而java8 提供的工具包早已看穿了這一切,(防盜連線:本文首發自http://www.cnblogs.com/jilodream/ )並提供了一套優雅的解決方案:BlockingQueue
BlockingQueue 是一個介面定義,我們實際使用的是下邊的各個實現類:

BlockingQueue 中常用的方法如下,記住這些程式碼不要死記,只要記得有這回事,需要的時候檢視一下原始碼即可:

除此之外還會有獲取指定元素(不移除)E element()/E peek(),刪除指定元素boolean remove(Object o)等方法這裡就先不贅述了。

下面我們來看一組簡單的示例
生產者生產三種禮物gift: 手機/電腦/LV包
消費者通通消費
生產者/消費者都使用執行緒池來維護,具體情況如下程式碼:

主類

 1 package com.example.demo.learnblockingqueue;
 2 
 3 import java.util.Comparator;
 4 import java.util.concurrent.ArrayBlockingQueue;
 5 import java.util.concurrent.BlockingQueue;
 6 import java.util.concurrent.DelayQueue;
 7 import java.util.concurrent.ExecutorService;
 8 import java.util.concurrent.Executors;
 9 import java.util.concurrent.LinkedBlockingQueue;
10 import java.util.concurrent.PriorityBlockingQueue;
11 import java.util.concurrent.SynchronousQueue;
12 
13 /**
14  * @discription
15  */
16 public class BlockQueueMain {
17     public static void main(String[] args) {
18 
19         BlockingQueue<Gift> queue = new ArrayBlockingQueue<>(3);
20         Gift phone = new Gift("iphone", 8888);
21         Producer phoneProducer = new Producer(queue, "AAAA  蘋果代工廠", phone);
22 
23         Gift computer = new Gift("lenovo", 12888);
24         Producer computerProducer = new Producer(queue, "AAAA  聯想代工廠", computer);
25 
26         Gift bag = new Gift("LV", 28888);
27         Producer bagProducer = new Producer(queue, "AAAA  LV代工廠", bag);
28 
29         Consumer zConsumer = new Consumer(queue, "BBBB  小Z");
30         Consumer lConsumer = new Consumer(queue, "BBBB  小L");
31 
32         ExecutorService executorService = Executors.newCachedThreadPool();
33         executorService.submit(phoneProducer);
34         executorService.submit(computerProducer);
35         executorService.submit(bagProducer);
36         executorService.submit(zConsumer);
37         executorService.submit(lConsumer);
38 
39     }
40 }

產品類

 1 package com.example.demo.learnblockingqueue;
 2 
 3 import lombok.AllArgsConstructor;
 4 import lombok.Data;
 5 import lombok.NoArgsConstructor;
 6 
 7 import java.util.Date;
 8 import java.util.concurrent.Delayed;
 9 import java.util.concurrent.TimeUnit;
10 
11 /**
12  * @discription
13  */
14 @Data
15 @NoArgsConstructor
16 public class Gift implements Delayed {
17 
18     private String name;
19 
20     private int price;
21 
22     private final long completeTime = new Date().getTime() + 3000;
23 
24     public Gift(Gift giftSample) {
25         name = giftSample.getName();
26         price = giftSample.getPrice();
27     }
28 
29     public Gift(String name, int price) {
30         this.name = name;
31         this.price = price;
32     }
33 
34     @Override
35     public long getDelay(TimeUnit unit) {
36 
37         return unit.convert(completeTime - new Date().getTime(), TimeUnit.MILLISECONDS);
38     }
39 
40     @Override
41     public int compareTo(Delayed o) {
42         return 0;
43     }
44 }

生產者:

 1 package com.example.demo.learnblockingqueue;
 2 
 3 import lombok.extern.slf4j.Slf4j;
 4 
 5 import java.util.concurrent.BlockingQueue;
 6 import java.util.concurrent.TimeUnit;
 7 
 8 /**
 9  * @discription
10  */
11 @Slf4j
12 public class Producer implements Runnable {
13 
14     private final BlockingQueue<Gift> queue;
15 
16     private final String name;
17 
18     private final Gift giftSample;
19 
20     public Producer(BlockingQueue<Gift> queue, String name, Gift giftSample) {
21         this.queue = queue;
22         this.name = name;
23         this.giftSample = giftSample;
24     }
25 
26     @Override
27     public void run() {
28 
29         while (true) {
30             Gift newGift = new Gift(giftSample);
31             try {
32                 log.warn(name + "開始生產:{}", newGift);
33                 queue.put(newGift);
34                 log.warn(name + "生產了:{}, 剩餘空間{}", newGift,  queue.remainingCapacity());
35 
36                 TimeUnit.MILLISECONDS.sleep(10000);
37             } catch (InterruptedException e) {
38                 log.error("producer  {} catch a ex", name, e);
39             }
40         }
41     }
42 }

消費者:

 1 package com.example.demo.learnblockingqueue;
 2 
 3 import lombok.extern.slf4j.Slf4j;
 4 
 5 import java.util.concurrent.BlockingQueue;
 6 import java.util.concurrent.TimeUnit;
 7 
 8 /**
 9  * @discription
10  */
11 @Slf4j
12 public class Consumer implements Runnable {
13 
14     private final String name;
15     private final BlockingQueue<Gift> queue;
16 
17 
18     public Consumer(BlockingQueue<Gift> queue, String name) {
19         this.queue = queue;
20         this.name = name;
21     }
22 
23     @Override
24     public void run() {
25         while (true) {
26 
27             try {
28                 Gift gift = queue.take();
29                 log.warn(name + "購買了: {} ,剩餘空間: {}", gift, queue.remainingCapacity());
30                 TimeUnit.MILLISECONDS.sleep(3000);
31             } catch (InterruptedException e) {
32                 log.error("Consumer  {} catch a ex", name, e);
33             }
34         }
35     }
36 }

執行後效果如下:

注意由於是多執行緒在操作,所以某一秒處的日誌可能是亂序的,

感興趣的同學可以自行分析下

17:13:37.477 [pool-1-thread-2] WARN com.example.demo.learnblockingqueue.Producer - AAAA  聯想代工廠開始生產:Gift(name=lenovo, price=12888, completeTime=1724750022471)
17:13:37.478 [pool-1-thread-1] WARN com.example.demo.learnblockingqueue.Producer - AAAA  蘋果代工廠開始生產:Gift(name=iphone, price=8888, completeTime=1724750022471)
17:13:37.478 [pool-1-thread-3] WARN com.example.demo.learnblockingqueue.Producer - AAAA  LV代工廠開始生產:Gift(name=LV, price=28888, completeTime=1724750022473)
17:13:37.486 [pool-1-thread-1] WARN com.example.demo.learnblockingqueue.Producer - AAAA  蘋果代工廠生產了:Gift(name=iphone, price=8888, completeTime=1724750022471), 剩餘空間1
17:13:37.486 [pool-1-thread-4] WARN com.example.demo.learnblockingqueue.Consumer - BBBB  小Z購買了: Gift(name=iphone, price=8888, completeTime=1724750022471) ,剩餘空間: 2
17:13:37.486 [pool-1-thread-5] WARN com.example.demo.learnblockingqueue.Consumer - BBBB  小L購買了: Gift(name=lenovo, price=12888, completeTime=1724750022471) ,剩餘空間: 2
17:13:37.486 [pool-1-thread-2] WARN com.example.demo.learnblockingqueue.Producer - AAAA  聯想代工廠生產了:Gift(name=lenovo, price=12888, completeTime=1724750022471), 剩餘空間2
17:13:37.486 [pool-1-thread-3] WARN com.example.demo.learnblockingqueue.Producer - AAAA  LV代工廠生產了:Gift(name=LV, price=28888, completeTime=1724750022473), 剩餘空間2
17:13:40.491 [pool-1-thread-4] WARN com.example.demo.learnblockingqueue.Consumer - BBBB  小Z購買了: Gift(name=LV, price=28888, completeTime=1724750022473) ,剩餘空間: 3
17:13:47.491 [pool-1-thread-3] WARN com.example.demo.learnblockingqueue.Producer - AAAA  LV代工廠開始生產:Gift(name=LV, price=28888, completeTime=1724750032491)
17:13:47.491 [pool-1-thread-1] WARN com.example.demo.learnblockingqueue.Producer - AAAA  蘋果代工廠開始生產:Gift(name=iphone, price=8888, completeTime=1724750032491)
17:13:47.491 [pool-1-thread-2] WARN com.example.demo.learnblockingqueue.Producer - AAAA  聯想代工廠開始生產:Gift(name=lenovo, price=12888, completeTime=1724750032491)
17:13:47.491 [pool-1-thread-2] WARN com.example.demo.learnblockingqueue.Producer - AAAA  聯想代工廠生產了:Gift(name=lenovo, price=12888, completeTime=1724750032491), 剩餘空間1
17:13:47.491 [pool-1-thread-3] WARN com.example.demo.learnblockingqueue.Producer - AAAA  LV代工廠生產了:Gift(name=LV, price=28888, completeTime=1724750032491), 剩餘空間2
17:13:47.491 [pool-1-thread-4] WARN com.example.demo.learnblockingqueue.Consumer - BBBB  小Z購買了: Gift(name=iphone, price=8888, completeTime=1724750032491) ,剩餘空間: 2
17:13:47.491 [pool-1-thread-1] WARN com.example.demo.learnblockingqueue.Producer - AAAA  蘋果代工廠生產了:Gift(name=iphone, price=8888, completeTime=1724750032491), 剩餘空間2
17:13:47.491 [pool-1-thread-5] WARN com.example.demo.learnblockingqueue.Consumer - BBBB  小L購買了: Gift(name=LV, price=28888, completeTime=1724750032491) ,剩餘空間: 2

接下來簡單說下BlockingQueue的幾種實現方式的內部邏輯和使用場景
1、ArrayBlockingQueue
內部使用陣列實現緩衝區,(防盜連線:本文首發自http://www.cnblogs.com/jilodream/ )有界佇列;
使用ReentrantLock鎖來保證執行緒安全
可以滿足大部分的業務場景。

2、LinkedBlockingQueue
內部使用了單連結串列實現了緩衝區,可以是無界佇列,也可以是有界佇列;
使用讀寫鎖來保證執行緒安全。
適用於佇列彈性比較大,併發性要求高的場景。

3、SynchronousQueue
同步佇列
很多人稱如果想要任務快速執行,可以使用該佇列。容易讓別人有誤解,不如我們來舉個實際的例子
將主類中的blockingqueue 切換為SynchronousQueue,

BlockingQueue<Gift> queue = new SynchronousQueue<>();

輸入為下,下面是前13秒是的輸出,紅色字型為我的解釋:

15:35:30.906 [pool-1-thread-3] WARN com.example.demo.learnblockingqueue.Producer - AAAA  LV代工廠開始生產:Gift(name=LV, price=28888, completeTime=1724744135902)
15:35:30.915 [pool-1-thread-3] WARN com.example.demo.learnblockingqueue.Producer - AAAA  LV代工廠生產了:Gift(name=LV, price=28888, completeTime=1724744135902), 剩餘空間0
15:35:30.907 [pool-1-thread-2] WARN com.example.demo.learnblockingqueue.Producer - AAAA  聯想代工廠開始生產:Gift(name=lenovo, price=12888, completeTime=1724744135901)
15:35:30.915 [pool-1-thread-5] WARN com.example.demo.learnblockingqueue.Consumer - BBBB  小L購買了: Gift(name=LV, price=28888, completeTime=1724744135902) ,剩餘空間: 0
15:35:30.906 [pool-1-thread-1] WARN com.example.demo.learnblockingqueue.Producer - AAAA  蘋果代工廠開始生產:Gift(name=iphone, price=8888, completeTime=1724744135901)
15:35:30.915 [pool-1-thread-2] WARN com.example.demo.learnblockingqueue.Producer - AAAA  聯想代工廠生產了:Gift(name=lenovo, price=12888, completeTime=1724744135901), 剩餘空間0
15:35:30.915 [pool-1-thread-4] WARN com.example.demo.learnblockingqueue.Consumer - BBBB  小Z購買了: Gift(name=lenovo, price=12888, completeTime=1724744135901) ,剩餘空間: 0
請求之後,工廠分別生產了3個產品:手機、電腦、LV
之後LV 和電腦 都被消費了,此時iPhone還屬於待消費狀態
15:35:33.916 [pool-1-thread-5] WARN com.example.demo.learnblockingqueue.Consumer - BBBB 小L購買了: Gift(name=iphone, price=8888, completeTime=1724744135901) ,剩餘空間: 0 15:35:33.916 [pool-1-thread-1] WARN com.example.demo.learnblockingqueue.Producer - AAAA 蘋果代工廠生產了:Gift(name=iphone, price=8888, completeTime=1724744135901), 剩餘空間0 消費者睡眠3秒鐘以後,消費了手機
此時手機的生產者才開始列印日誌,標明此時put方法的阻塞狀態才結束

注意最後的時間點 15:35:33.916

消費者第二次消費完,iPhone的廠商立刻列印了日誌,所以之前iPhone的廠商在放置資料的過程中,一直處於一個阻塞的狀態。
也就是如果消費者不取,那麼生產者就一直處於呈現狀態。這恰恰是如果是快速消費的場景,可以使用此方案,而不是此方案可以讓你的任務快速執行。當然我們透過名字其實也可以判斷。

這種場景現實中有麼?
也是有的,比如服務員找零,一般會將錢放在手裡,等你拿了錢才會做接下來的動作。像這種需要同步交接,快速響應的場景,那麼就可以考慮SynchronousQueue。
當然實際場景中,服務員也不可能一直等著,消費者可能一直不拿錢(超時設定),消費者不收錢(透過InterruptedException 打斷阻塞)。

4、PriorityBlockingQueue
優先順序的阻塞佇列,想法其實很簡單,就是消費者不再是先進先出的獲取資料,而是允許根據優先順序排序後,優先拿優先順序高的產品。佇列內部是透過最小堆來維護佇列的,我們在建立佇列時,
要指定比較演算法。如下,我們使用的是按照價格由高到低的方式獲取產品,同時調整生產者速度>消費者速度(調整生產者sleep 為1000ms即可):

        BlockingQueue<Gift> queue = new PriorityBlockingQueue<>(3, (o1, o2) -> o2.getPrice() - o1.getPrice());

輸出結果如下,我們可以看到消費者只獲取當前佇列中價值最高的產品:

18:39:01.040 [pool-1-thread-1] WARN com.example.demo.learnblockingqueue.Producer - AAAA  蘋果代工廠開始生產:Gift(name=iphone, price=8888, completeTime=1724755146036)
18:39:01.040 [pool-1-thread-2] WARN com.example.demo.learnblockingqueue.Producer - AAAA  聯想代工廠開始生產:Gift(name=lenovo, price=12888, completeTime=1724755146036)
18:39:01.040 [pool-1-thread-3] WARN com.example.demo.learnblockingqueue.Producer - AAAA  LV代工廠開始生產:Gift(name=LV, price=28888, completeTime=1724755146036)
18:39:01.045 [pool-1-thread-3] WARN com.example.demo.learnblockingqueue.Producer - AAAA  LV代工廠生產了:Gift(name=LV, price=28888, completeTime=1724755146036),
18:39:01.045 [pool-1-thread-4] WARN com.example.demo.learnblockingqueue.Consumer - BBBB  小Z購買了: Gift(name=LV, price=28888, completeTime=1724755146036) ,
18:39:01.045 [pool-1-thread-2] WARN com.example.demo.learnblockingqueue.Producer - AAAA  聯想代工廠生產了:Gift(name=lenovo, price=12888, completeTime=1724755146036), 
18:39:01.045 [pool-1-thread-5] WARN com.example.demo.learnblockingqueue.Consumer - BBBB  小L購買了: Gift(name=lenovo, price=12888, completeTime=1724755146036) ,
18:39:01.045 [pool-1-thread-1] WARN com.example.demo.learnblockingqueue.Producer - AAAA  蘋果代工廠生產了:Gift(name=iphone, price=8888, completeTime=1724755146036), 
18:39:02.059 [pool-1-thread-3] WARN com.example.demo.learnblockingqueue.Producer - AAAA  LV代工廠開始生產:Gift(name=LV, price=28888, completeTime=1724755147059)
...省略一部分日誌....
18:39:03.069 [pool-1-thread-3] WARN com.example.demo.learnblockingqueue.Producer - AAAA  LV代工廠生產了:Gift(name=LV, price=28888, completeTime=1724755148069), 
18:39:03.069 [pool-1-thread-2] WARN com.example.demo.learnblockingqueue.Producer - AAAA  聯想代工廠生產了:Gift(name=lenovo, price=12888, completeTime=1724755148069), 
18:39:04.052 [pool-1-thread-4] WARN com.example.demo.learnblockingqueue.Consumer - BBBB  小Z購買了: Gift(name=LV, price=28888, completeTime=1724755148069) ,
18:39:04.052 [pool-1-thread-5] WARN com.example.demo.learnblockingqueue.Consumer - BBBB  小L購買了: Gift(name=LV, price=28888, completeTime=1724755147059) ,

5、DelayQueue

這個佇列也很有意思,它需要有產品實現一個delay介面,介面實時的返回還要多久這個產品可用。
如下我們切換為 DelayQueue。
效果如下:
消費者即使已經觸發獲取,(防盜連線:本文首發自http://www.cnblogs.com/jilodream/ )但是還是無法立刻拿到,需要在delay()返回非正數以後才能獲取。這樣生產者可以生產一些內部有時間概念的產品,起到一個定時器的作用。
舉個例子:
我們裝修了一間房子,由於裝修有害物質,我們需要6個月後才能入住,

常用的辦法如下:
1、新增定時器,由定時器來觸發,但是顯然要有外部依賴其他物件。
2、反覆獲取,拿到之後check時間,如果未到時間,重新丟回佇列,但是這樣顯然過於複雜,系統的呼叫頻次也會上去。
3、消費者來獲取產品時,判斷時間是否可取,如果不可取就一直等待到可取到產品為止。注意此時消費執行緒會阻塞到該佇列頭部的產品,並不會越過它嘗試拿後續的產品。(DelayQueue選用此種策略)

我們先修改緩衝佇列為DelayQueue

BlockingQueue<Gift> queue = new DelayQueue<>();

生產者的時間週期改為2s,消費者的時間週期改為4s ,假設產品的過期時間是5s注意這幾個時間節點,

Gift 類的程式碼我們需要改造一下,方便定位分析:

 1 package com.example.demo.learnblockingqueue;
 2 
 3 import lombok.Data;
 4 import lombok.NoArgsConstructor;
 5 import lombok.extern.slf4j.Slf4j;
 6 
 7 import java.util.Date;
 8 import java.util.concurrent.Delayed;
 9 import java.util.concurrent.TimeUnit;
10 
11 /**
12  * @discription
13  */
14 @Data
15 @NoArgsConstructor
16 @Slf4j
17 public class Gift implements Delayed {
18 
19     private String name;
20 
21     private int price;
22 
23     private final long completeTime = new Date().getTime() + 5000;
24 
25     public Gift(Gift giftSample) {
26         name = giftSample.getName() + "_" + completeTime;
27         price = giftSample.getPrice();
28     }
29 
30     public Gift(String name, int price) {
31         this.name = name;
32         this.price = price;
33     }
34 
35     private void printLog() {
36         log.warn(Thread.currentThread() + "     !!!!"+ "_" + name);
37     }
38 
39     @Override
40     public long getDelay(TimeUnit unit) {
41         printLog();
42         return unit.convert(completeTime - new Date().getTime(), TimeUnit.MILLISECONDS);
43     }
44 
45     @Override
46     public int compareTo(Delayed o) {
47         return 0;
48     }
49 }

效果大概是這樣的:

是不是和我們設想的不太一樣,直接說發生了什麼,注意看不同顏色的分析對應的不同的顏色的日誌:

首先是執行緒5 消費者搶到了鎖,則直接等待到產品到期,注意執行緒5不是按照我們約定的週期檢查的,然後二次確認時間,接著就消費了(24秒--->29秒)

此時執行緒4搶到了下一把鎖注意下一個禮物是最新生產的產品,而不是最早生產的產品然後等待產品到期時間,二次確認並消費(28秒--->33秒)注意在二次確認的時間的時候,兩個執行緒都有確認,但是隻有一個執行緒真正會消費成功,這不影響使用效果(猜測這裡是為了提高效能,並沒有完全,沒有采用很重的鎖)。

接下來的邏輯一樣

Connected to the target VM, address: '127.0.0.1:54328', transport: 'socket'
19:52:24.292 [pool-1-thread-3] WARN com.example.demo.learnblockingqueue.Producer - AAAA  LV代工廠開始生產:Gift(name=LV_1724759549288, price=28888, completeTime=1724759549288)
19:52:24.292 [pool-1-thread-2] WARN com.example.demo.learnblockingqueue.Producer - AAAA  聯想代工廠開始生產:Gift(name=lenovo_1724759549289, price=12888, completeTime=1724759549289)
19:52:24.292 [pool-1-thread-1] WARN com.example.demo.learnblockingqueue.Producer - AAAA  蘋果代工廠開始生產:Gift(name=iphone_1724759549288, price=8888, completeTime=1724759549288)
19:52:24.300 [pool-1-thread-5] WARN com.example.demo.learnblockingqueue.Gift - Thread[pool-1-thread-5,5,main]     !!!!_LV_1724759549288
19:52:24.300 [pool-1-thread-3] WARN com.example.demo.learnblockingqueue.Producer - AAAA  LV代工廠生產了:Gift(name=LV_1724759549288, price=28888, completeTime=1724759549288), 
19:52:24.300 [pool-1-thread-2] WARN com.example.demo.learnblockingqueue.Producer - AAAA  聯想代工廠生產了:Gift(name=lenovo_1724759549289, price=12888, completeTime=1724759549289),
19:52:24.302 [pool-1-thread-1] WARN com.example.demo.learnblockingqueue.Producer - AAAA  蘋果代工廠生產了:Gift(name=iphone_1724759549288, price=8888, completeTime=1724759549288), 
..省略..
19:52:28.321 [pool-1-thread-3] WARN com.example.demo.learnblockingqueue.Producer - AAAA  LV代工廠生產了:Gift(name=LV_1724759553321, price=28888, completeTime=1724759553321), 
19:52:28.321 [pool-1-thread-2] WARN com.example.demo.learnblockingqueue.Producer - AAAA  聯想代工廠生產了:Gift(name=lenovo_1724759553321, price=12888, completeTime=1724759553321), 
19:52:28.321 [pool-1-thread-1] WARN com.example.demo.learnblockingqueue.Producer - AAAA  蘋果代工廠生產了:Gift(name=iphone_1724759553321, price=8888, completeTime=1724759553321), 
19:52:29.300 [pool-1-thread-5] WARN com.example.demo.learnblockingqueue.Gift - Thread[pool-1-thread-5,5,main]     !!!!_LV_1724759549288
19:52:29.300 [pool-1-thread-4] WARN com.example.demo.learnblockingqueue.Gift - Thread[pool-1-thread-4,5,main]     !!!!_iphone_1724759553321
19:52:29.300 [pool-1-thread-5] WARN com.example.demo.learnblockingqueue.Consumer - BBBB  小L購買了: Gift(name=LV_1724759549288, price=28888, completeTime=1724759549288) ,
19:52:30.326 [pool-1-thread-3] WARN com.example.demo.learnblockingqueue.Producer - AAAA  LV代工廠開始生產:Gift(name=LV_1724759555326, price=28888, completeTime=1724759555326)
19:52:30.326 [pool-1-thread-1] WARN com.example.demo.learnblockingqueue.Producer - AAAA  蘋果代工廠開始生產:Gift(name=iphone_1724759555326, price=8888, completeTime=1724759555326)
...省略..
19:52:32.334 [pool-1-thread-1] WARN com.example.demo.learnblockingqueue.Producer - AAAA  蘋果代工廠開始生產:Gift(name=iphone_1724759557334, price=8888, completeTime=1724759557334)
19:52:32.334 [pool-1-thread-3] WARN com.example.demo.learnblockingqueue.Producer - AAAA  LV代工廠生產了:Gift(name=LV_1724759557334, price=28888, completeTime=1724759557334), 
19:52:32.334 [pool-1-thread-1] WARN com.example.demo.learnblockingqueue.Producer - AAAA  蘋果代工廠生產了:Gift(name=iphone_1724759557334, price=8888, completeTime=1724759557334),
19:52:32.334 [pool-1-thread-2] WARN com.example.demo.learnblockingqueue.Producer - AAAA  聯想代工廠生產了:Gift(name=lenovo_1724759557334, price=12888, completeTime=1724759557334), 
19:52:33.301 [pool-1-thread-5] WARN com.example.demo.learnblockingqueue.Gift - Thread[pool-1-thread-5,5,main]     !!!!_iphone_1724759553321
19:52:33.331 [pool-1-thread-4] WARN com.example.demo.learnblockingqueue.Gift - Thread[pool-1-thread-4,5,main]     !!!!_iphone_1724759553321
19:52:33.331 [pool-1-thread-5] WARN com.example.demo.learnblockingqueue.Gift - Thread[pool-1-thread-5,5,main]     !!!!_iphone_1724759557334
19:52:33.331 [pool-1-thread-4] WARN com.example.demo.learnblockingqueue.Consumer - BBBB  小Z購買了: Gift(name=iphone_1724759553321, price=8888, completeTime=1724759553321) ,
19:52:34.346 [pool-1-thread-2] WARN com.example.demo.learnblockingqueue.Producer - AAAA  聯想代工廠開始生產:Gift(name=lenovo_1724759559346, price=12888, completeTime=1724759559346)
19:52:34.346 [pool-1-thread-3] WARN com.example.demo.learnblockingqueue.Producer - AAAA  LV代工廠開始生產:Gift(name=LV_1724759559346, price=28888, completeTime=1724759559346)
19:52:34.346 [pool-1-thread-1] WARN com.example.demo.learnblockingqueue.Producer - AAAA  蘋果代工廠開始生產:Gift(name=iphone_1724759559346, price=8888, completeTime=1724759559346)
...省略..
19:52:36.361 [pool-1-thread-3] WARN com.example.demo.learnblockingqueue.Producer - AAAA  LV代工廠生產了:Gift(name=LV_1724759561361, price=28888, completeTime=1724759561361),
19:52:36.361 [pool-1-thread-2] WARN com.example.demo.learnblockingqueue.Producer - AAAA  聯想代工廠生產了:Gift(name=lenovo_1724759561361, price=12888, completeTime=1724759561361), 
19:52:36.361 [pool-1-thread-1] WARN com.example.demo.learnblockingqueue.Producer - AAAA  蘋果代工廠生產了:Gift(name=iphone_1724759561361, price=8888, completeTime=1724759561361), 
Disconnected from the target VM, address: '127.0.0.1:54328', transport: 'socket'(防盜連線:本文首發自http://www.cnblogs.com/jilodream/ )
19:52:37.339 [pool-1-thread-5] WARN com.example.demo.learnblockingqueue.Gift - Thread[pool-1-thread-5,5,main]     !!!!_iphone_1724759557334
19:52:37.340 [pool-1-thread-4] WARN com.example.demo.learnblockingqueue.Gift - Thread[pool-1-thread-4,5,main]     !!!!_iphone_1724759561361
19:52:37.339 [pool-1-thread-5] WARN com.example.demo.learnblockingqueue.Consumer - BBBB  小L購買了: Gift(name=iphone_1724759557334, price=8888, c

以上我們可以得到兩個重要結論:

DelayQueue並不是按照順序進行消費的,而是獲取最新的的資料進行嘗試獲取,(儘管此時佇列中已經存在可用的產品)

DelayQueue喚醒消費者的時間,是按照產品的到達可用狀態的時間後,才會喚醒

因此使用DelayQueue時,我們要注意,生產商品不能太激烈,生產速度不要大於消費速度,否則產品很可能消費不到了(當然也可以利用這個規則,將舊的產品進行淘汰處理)

產品的delay時間不能太長,否則可能會導致消費者一直處於掛起狀態。

相關文章