SynchronousQueue
特點
-
是阻塞隊列BlockingQueue的一種實現,也就是方法具有BlokingQueue的基本性質;
-
和ArrayBlockingQueue區別在於,ArrayBlockingQueue內部會有一個數組用於存放元素,相當於一個緩存區;而對於SynchronousQueue,內部沒有這樣的數據結構用於存放數據。
-
對於具體的一個線程A,想要插入數據,必須有另一個線程B打算取數據,否則線程A阻塞(底層是調用Unsafe.park導致阻塞的)。這樣實現的目的就是希望生產數據的速度和消費數據的速度步調保持一致。
主要的數據結構
- Transferer
Shared internal API for dual stacks and queues
上面這個接口保存了數據轉移的關鍵api,上面英文轉爲中文應該表示爲雙棧和雙隊列的共享內部api,“雙”主要體檢在當取數據時,如果沒有線程等待存數據,會創建一個表示取數據的Node節點。如果存數據時,如果沒有等待取數據的線程,會創建一個表示存數據的Node節點。對於這兩類節點都是用同一個結構表示的。這鏈表中,同一時刻只能有一個一種類型的節點存在。
Transferer接口的兩個實現類:
-
TransferStack
用於實現非公平方式的存取數據。
-
TransferQueue
用於實現公平方式的存取數據。
Demo
import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.SynchronousQueue;
public class Test {
public static void main(String[] args) throws Exception {
SynchronousQueue<Integer> queue = new SynchronousQueue<>(true);
new Thread(() -> {
try {
queue.put(1);
}catch (InterruptedException e) {}
}, "producer1").start();
new Thread(() -> {
try {
queue.put(2);
}catch (InterruptedException e) {}
}, "producer2").start();
new Thread(() -> {
try {
System.out.println(queue.take());
}catch (InterruptedException e) {}
}, "consumer1").start();
}
}
結果分析:
輸出結果可能是1或者2,兩個線程負責各插入一個數據,一個線程取數據。執行完後,會出現一個線程阻塞。因爲嘗試插入了兩個數,卻只有一個數被消費。除非再消費一次,才能解除阻塞的線程。