java併發程式設計工具類JUC第六篇:SynchronousQueue同步佇列
在之前的文章中已經為大家介紹了java併發程式設計的工具:BlockingQueue介面、ArrayBlockingQueue、DelayQueue、LinkedBlockingQueue、PriorityBlockingQueue,本文為系列文章第六篇。
本篇文章將為大家介紹併發程式設計集合類SynchronousQueue,它是BlockingQueue介面的實現類。與所有的BlockingQueue介面實現類不同的是:SynchronousQueue佇列的容量永遠是0(或者可以理解為容量為1的佇列,但是說佇列容量為1並不準確),這是因為SynchronousQueue實際上它不是一個真正的佇列,因為它不會為佇列中元素維護儲存空間,它只是多個執行緒之間資料交換的媒介。
SynchronousQueue
佇列的一個執行緒的插入動作總是會等待另一個執行緒的移除操作,反之亦然。put()
方法放入元素物件到 SynchronousQueue不會返回結果(處於阻塞狀態),直到另一個執行緒執行take()
移除元素物件.peek()
方法在BlockingQueue介面實現類中可以獲取元素,但不從佇列中移除該元素。但在SynchronousQueue佇列中該方法不可用。
SynchronousQueue 同步佇列例子
下面我們寫一個例子,來幫助我們理解SynchronousQueue
的特性及用法。 SynchronousQueueProducer.java 啟動執行緒每隔一秒向佇列中放入一個Integer物件。
public class SynchronousQueueProducer implements Runnable {
protected BlockingQueue<Integer> blockingQueue;
public SynchronousQueueProducer(BlockingQueue<Integer> queue) {
this.blockingQueue = queue;
}
[@Override](http://my.oschina.net/u/1162528)
public void run() {
int i = 0;
while (true) {
System.out.println(Thread.currentThread().getName() + " Put: " + ++i);
try {
blockingQueue.put(i);
Thread.sleep(1000); //每隔一秒生產一次
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
SynchronousQueueConsumer.java啟動執行緒每5秒從佇列中取出一個元素物件。
public class SynchronousQueueConsumer implements Runnable {
protected BlockingQueue<Integer> blockingQueue;
public SynchronousQueueConsumer(BlockingQueue<Integer> queue) {
this.blockingQueue = queue;
}
[@Override](http://my.oschina.net/u/1162528)
public void run() {
while (true) {
try {
Integer data = blockingQueue.take();
System.out.println(Thread.currentThread().getName() + " take(): " + data);
Thread.sleep(5000); //每隔5秒消費一次
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
SynchronousQueueExample.java 新建一個SynchronousQueue同步佇列,啟動一個生產者執行緒插入物件,兩個消費者執行緒移除物件。進行測試,看看效果。
public class SynchronousQueueExample {
public static void main(String[] args) {
final BlockingQueue<Integer> synchronousQueue = new SynchronousQueue<>();
SynchronousQueueProducer queueProducer = new SynchronousQueueProducer(synchronousQueue);
new Thread(queueProducer).start();
SynchronousQueueConsumer queueConsumer1 = new SynchronousQueueConsumer(synchronousQueue);
new Thread(queueConsumer1).start();
SynchronousQueueConsumer queueConsumer2 = new SynchronousQueueConsumer(synchronousQueue);
new Thread(queueConsumer2).start();
}
}
分析一下實驗的結果,Thread-0 是生產者執行緒,Thread-1 和 Thread-2是消費者執行緒。從實驗結果我們可以看到SynchronousQueue必須是生產一次、消費一次、生產一次、消費一次,不論有多少個消費者和生產者執行緒都必須遵循這個規則。
Thread-0 Put: 1
Thread-1 take(): 1
Thread-0 Put: 2
Thread-2 take(): 2
Thread-0 Put: 3
Thread-1 take(): 3
Thread-0 Put: 4
Thread-2 take(): 4
Thread-0 Put: 5
Thread-1 take(): 5
Thread-0 Put: 6
Thread-2 take(): 6
Thread-0 Put: 7
Thread-1 take(): 7
Thread-0 Put: 8
Thread-2 take(): 8
Thread-0 Put: 9
Thread-1 take(): 9
Thread-0 Put: 10
Thread-2 take(): 10
Thread-0 Put: 11
Thread-1 take(): 11
Thread-0 Put: 12
Thread-2 take(): 12
應用場景:如果我們不確定來自生產者請求數量,但是這些請求需要很快的處理掉,那麼配合SynchronousQueue為每個生產者請求分配一個消費執行緒是最處理效率最高的辦法。Executors.newCachedThreadPool()底層就使用了SynchronousQueue,這個執行緒池根據需求建立新的執行緒。
歡迎關注我的部落格,裡面有很多精品合集
本文轉載註明出處(必須帶連線,不能只轉文字):字母哥部落格 - zimug.com
覺得對您有幫助的話,幫我點贊、分享!您的支援是我不竭的創作動力! 。另外,筆者最近一段時間輸出瞭如下的精品內容,期待您的關注。
- 長篇圖解java反射機制及其應用場景
- 併發程式設計系列之Lock鎖可重入性與公平性
- java併發程式設計JUC第十二篇:AtomicInteger原子整型
- java併發程式設計JUC第十一篇:如何線上程之間進行對等資料交換
- java併發程式設計JUC第十篇:CyclicBarrier執行緒同步
- java併發程式設計JUC第九篇:CountDownLatch執行緒同步
- java併發程式設計JUC第八篇:ConcurrentHashMap
- java併發程式設計工具類JUC第七篇:BlockingDeque雙端阻塞佇列
- java併發程式設計工具類JUC第六篇:SynchronousQueue同步佇列
- java併發程式設計工具類JUC第五篇:PriorityBlockingQueue優先順序佇列
- java併發程式設計工具類JUC第四篇:LinkedBlockingQueue連結串列佇列
- java併發程式設計工具類JUC第三篇:DelayQueue延時佇列
- java併發程式設計工具類JUC第二篇:ArrayBlockingQueue
- java併發程式設計工具類JUC第一篇:BlockingQueue阻塞佇列
- 快速入門Redis呼叫Lua指令碼及使用場景介紹
- Mybatis plus通用欄位自動填充的最佳實踐總結
- 詳解執行緒池的作用及Java中如何使用執行緒池
- Java9系列第九篇-對HTTP2協議的支援與非阻塞HTTP-API
- 跨站資源共享CORS原理深度解析
- 你以前看的執行緒狀態轉換圖可能都是錯的-圖解併發程式設計第二篇