阻塞佇列 SynchronousQueue 原始碼解析
SynchronousQueue
- 特性
- SynchronousQueue 沒有容量。與其他 BlockingQueue 不同,SynchronousQueue 是一個不儲存元素的 BlockingQueue。可以有多個 put ,但每一個put操作必須要等待一個take操作,否則該執行緒會一直阻塞在 put,反之亦然。
- 因為沒有容量,所以對應 peek, contains, clear, isEmpty … 等方法其實是無效的。例如 clear 是不執行任何操作的,contains 始終返回 false,peek 始終返回 null。
- SynchronousQueue 分為公平和非公平,預設情況下采用非公平性訪問策略,當然也可以通過建構函式來設定為公平性訪問策略(為true即可)。
- 底層
- 每個節點都有自己的模式,用來判斷是請求節點(出隊請求)還是資料節點(入隊請求),如果隊首節點的模式和這次 transfer 的模式不同,那就匹配上了,那麼就可以讓隊首出隊並喚醒阻塞在隊首上的執行緒,如果不匹配那麼就如入隊等待。注意正是因為這種特性,佇列中等待的節點永遠可以保證是同一模式。
- 公平:用佇列實現,如果是同種模式要入隊阻塞,那麼就放到隊尾(casNext)
- 非公平:用棧實現,如果是同種模式要入棧阻塞,因為棧的特性,是放到第一個位置(casHead),所以後入棧,但先出棧匹配
- 併發安全是通過 CAS 保證併發安全,入隊等待(模式相同,自旋 + casNext ,失敗則從迴圈開始處重新執行)和節點匹配(模式不同,自旋 + casItem,節點的 item 屬性是兩個執行緒資料交換的場地,一旦併發競爭這個節點被其他執行緒搶先匹配成功,item 就會 cas 失敗,那麼就也是從迴圈開始處再來,下次不一定還會模式不同,因為一旦 cas 成功會讓被匹配的節點出隊,所以不存在下次迴圈還匹配這次失敗的節點)。阻塞是通過 LockSupport
- 每個節點都有自己的模式,用來判斷是請求節點(出隊請求)還是資料節點(入隊請求),如果隊首節點的模式和這次 transfer 的模式不同,那就匹配上了,那麼就可以讓隊首出隊並喚醒阻塞在隊首上的執行緒,如果不匹配那麼就如入隊等待。注意正是因為這種特性,佇列中等待的節點永遠可以保證是同一模式。
Transferer
定義
abstract static class Transferer<E> {
// 如果e != null,相當於將一個資料交給消費者
// 如果e == null,則相當於從一個生產者接收一個消費者交出的資料。
abstract E transfer(E e, boolean timed, long nanos);
}
TransferQueue
TransferQueue是實現公平性策略的核心類,其節點為QNode
static final class TransferQueue<E> extends Transferer<E> {
/** 頭節點 */
transient volatile QNode head;
/** 尾節點 */
transient volatile QNode tail;
// 指向一個取消的結點
//當一個節點中最後一個插入時,它被取消了但是可能還沒有離開佇列
transient volatile QNode cleanMe;
TransferQueue() {
QNode h = new QNode(null, false); // initialize to dummy node.
head = h;
tail = h;
}
static final class QNode {
// next 域
volatile QNode next;
// item資料項
volatile Object item;
// 等待執行緒,用於park/unpark
volatile Thread waiter;
//模式,表示當前是資料還是請求,只有當匹配的模式相匹配時才會交換
final boolean isData;
QNode(Object item, boolean isData) {
this.item = item;
this.isData = isData;
}
// CAS next域,在TransferQueue中用於向next推進
boolean casNext(QNode cmp, QNode val) {
return next == cmp &&
UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
}
// CAS itme資料項
boolean casItem(Object cmp, Object val) {
return item == cmp &&
UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
}
// 取消本結點,將item域設定為自身作為標誌
void tryCancel(Object cmp) {
UNSAFE.compareAndSwapObject(this, itemOffset, cmp, this);
}
// 是否被取消,與tryCancel相照應只需要判斷item釋放等於自身即可
boolean isCancelled() {
return item == this;
}
boolean isOffList() {
return next == this;
}
private static final sun.misc.Unsafe UNSAFE;
private static final long itemOffset;
private static final long nextOffset;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> k = QNode.class;
itemOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("item"));
nextOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("next"));
} catch (Exception e) {
throw new Error(e);
}
}
}
/**
* 省略很多程式碼
*/
}
TransferStack
TransferStack用於實現非公平性
static final class TransferStack<E> extends Transferer<E> {
// 三種 SNode 的 mode
// REQUEST表示消費資料的消費者
static final int REQUEST = 0;
// DATA表示生產資料的生產者
static final int DATA = 1;
// FULFILLING,表示匹配另一個生產者或消費者
static final int FULFILLING = 2;
volatile SNode head;
static final class SNode {
// next 域
volatile SNode next;
// 相匹配的節點
volatile SNode match;
// 等待的執行緒
volatile Thread waiter;
// item 域
Object item; // data; or null for REQUESTs
// 模式
int mode;
SNode(Object item) { this.item = item; }
boolean casNext(SNode cmp, SNode val) {
return cmp == next && UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
}
// 將 s 結點設定成匹配的節點,若匹配成功,則unpark等待執行緒
boolean tryMatch(SNode s) {
if (match == null &&
UNSAFE.compareAndSwapObject(this, matchOffset, null, s)) {
Thread w = waiter;
if (w != null) { // waiters need at most one unpark
waiter = null;
LockSupport.unpark(w);
}
return true;
}
return match == s;
}
// cancle 是把 match 的物件設定自己
void tryCancel() { UNSAFE.compareAndSwapObject(this, matchOffset, null, this); }
boolean isCancelled() { return match == this; }
// Unsafe mechanics
private static final sun.misc.Unsafe UNSAFE;
private static final long matchOffset;
private static final long nextOffset;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> k = SNode.class;
matchOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("match"));
nextOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("next"));
} catch (Exception e) {
throw new Error(e);
}
}
}
/**
* 省略一堆程式碼
*/
}
構造方法
public class SynchronousQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable{
public SynchronousQueue() {
this(false);
}
public SynchronousQueue(boolean fair) {
// 通過 fair 值來決定公平性和非公平性
// 公平性使用TransferQueue,非公平性採用TransferStack
transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
}
//...
}
put & get
都是直接呼叫的 transfer
// put操作
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
if (transferer.transfer(e, false, 0) == null) {
Thread.interrupted();
throw new InterruptedException();
}
}
// take操作
public E take() throws InterruptedException {
E e = transferer.transfer(null, false, 0);
if (e != null)
return e;
Thread.interrupted();
throw new InterruptedException();
}
TransferQueue
E transfer(E e, boolean timed, long nanos) {
QNode s = null;
// 當前請求模式
boolean isData = (e != null);
for (;;) {
QNode t = tail;
QNode h = head;
// 頭、尾節點 為null,沒有初始化
if (t == null || h == null)
continue;
// 頭尾節點相等(佇列為null) 或者當前節點和佇列節點模式一樣,那麼就要入隊等待了
if (h == t || t.isData == isData) {
// tn = t.next
QNode tn = t.next;
// t != tail表示已有其他執行緒操作了,修改了tail,重新再來
if (t != tail)
continue;
// tn != null,表示已經有其他執行緒新增了節點,tn 推進,重新處理
if (tn != null) {
// 當前執行緒幫忙推進尾節點,就是嘗試將tn設定為尾節點
advanceTail(t, tn);
continue;
}
// 呼叫的方法的 wait 型別的, 並且 超時了, 直接返回 null
// timed 在take操作闡述
if (timed && nanos <= 0)
return null;
// s == null,構建一個新節點Node
if (s == null)
s = new QNode(e, isData);
// 將新建的節點加入到佇列中,如果插入失敗,則重新自旋
if (!t.casNext(null, s))
continue;
// 替換尾節點(不成功無所謂的,因為連線時會找到真正的尾節點,這個只是一個推進作用)
advanceTail(t, s);
// 呼叫 awaitFulfill, 進行阻塞,並等待返回結果
Object x = awaitFulfill(s, e, timed, nanos);
// 若返回的 x == s,表示當前執行緒已經超時或者中斷,不然的話s == null 或者 是匹配的節點
if (x == s) {
// 清理節點S
clean(t, s);
return null;
}
// isOffList:用於判斷節點是否已經從佇列中離開了
if (!s.isOffList()) {
// 嘗試將S節點設定為head,移出t
advanceHead(t, s);
if (x != null)
s.item = s;
// 釋放執行緒 ref
s.waiter = null;
}
// 返回
return (x != null) ? (E)x : e;
}
// 這裡是就說明這個請求的模式 和佇列裡節點的模式不同,那麼就可以匹配了
else {
// 第一個元素節點(head 指向的節點並不是第一個元素節點,可能是初始化節點或者已經匹配完了的節點)
QNode m = h.next;
// 不一致讀,重新開始
// 有其他執行緒更改了執行緒結構
if (t != tail || m == null || h != head)
continue;
/**
* 下面是生產者producer和消費者consumer匹配操作
*/
Object x = m.item;
// isData == (x != null):判斷isData與x的模式是否相同,相同表示已經匹配了
// x == m :m節點被取消了(取消的標誌)
// !m.casItem(x, e):如果嘗試將資料e設定到m上失敗,說明這個節點被其他執行緒匹配過了
if (isData == (x != null) || x == m || !m.casItem(x, e)) {
// 將m設定為頭結點,h出列,再從頭開始
advanceHead(h, m);
continue;
}
// 成功匹配了,m 設定為頭結點h出列,向前推進
advanceHead(h, m);
// 喚醒 m 上的等待執行緒
LockSupport.unpark(m.waiter);
return (x != null) ? (E)x : e;
}
}
}
Object awaitFulfill(QNode s, E e, boolean timed, long nanos) {
// 超時控制
final long deadline = timed ? System.nanoTime() + nanos : 0L;
Thread w = Thread.currentThread();
// 自旋次數
// 如果節點Node恰好是第一個資料節點,則自旋一段時間,這裡主要是為了效率問題,如果裡面阻塞,會存在喚醒、執行緒上下文切換的問題
// 如果生產者、消費者者裡面到來的話,就避免了這個阻塞的過程
int spins = ((head.next == s) ?
(timed ? maxTimedSpins : maxUntimedSpins) : 0);
// 自旋
for (;;) {
// 執行緒中斷了,剔除當前節點
if (w.isInterrupted())
s.tryCancel(e);
// 如果被其他執行緒匹配了,直接返回當前 item 即可
// 實際上 item 就是 put 、get 進行資料交換的地方
Object x = s.item;
if (x != e)
return x;
// 超時判斷
if (timed) {
nanos = deadline - System.nanoTime();
// 如果超時了,取消節點,continue,在if(x != e)肯定會成立,直接返回x
if (nanos <= 0L) {
s.tryCancel(e);
continue;
}
}
// 自旋- 1
if (spins > 0)
--spins;
// 等待執行緒
else if (s.waiter == null)
s.waiter = w;
// 進行沒有超時的 park
else if (!timed)
LockSupport.park(this);
// 自旋次數過了, 直接 + timeout 方式 park
else if (nanos > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanos);
}
}
TransferStack
和 TransferQueue 區別不大,只是變成了每次模式相同入隊是從頭入
E transfer(E e, boolean timed, long nanos) {
SNode s = null; // constructed/reused as needed
int mode = (e == null) ? REQUEST : DATA;
for (;;) {
SNode h = head;
// 棧為空 或者 當前節點模式與頭節點模式一樣,將節點壓入棧內,等待匹配
if (h == null || h.mode == mode) {
// 超時
if (timed && nanos <= 0) {
// 節點被取消了,向前推進
if (h != null && h.isCancelled())
// 重新設定頭結點(彈出之前的頭結點)
casHead(h, h.next);
else
return null;
}
// 不超時
// 生成一個SNode節點,並嘗試替換掉頭節點head (head -> s)
// 頭插失敗的話還會重新自旋
else if (casHead(h, s = snode(s, e, h, mode))) {
// 自旋,等待匹配成功的節點喚醒
SNode m = awaitFulfill(s, timed, nanos);
// 返回的m == s 表示該節點被取消了或者超時、中斷了
if (m == s) {
// 清理節點S,return null
clean(s);
return null;
}
// 因為通過前面一步頭插成功 s 成了 head ,如果h.next == s ,則表示有其他節點插入到 s 前面了,
// 且該節點就是與節點 s 匹配的節點
if ((h = head) != null && h.next == s)
// 將s.next節點設定為head,相當於取消節點h、s
casHead(h, s.next);
// 如果是請求則返回匹配的域,否則返回節點 S 的域
return (E) ((mode == REQUEST) ? m.item : s.item);
}
}
// 如果棧不為null,且兩者模式不匹配(h != null && h.mode != mode)
// 說明他們是一隊對等匹配的節點,嘗試用當前節點s來滿足h節點
else if (!isFulfilling(h.mode)) {
// head 節點已經取消了,向前推進
if (h.isCancelled())
casHead(h, h.next);
// 嘗試將當前節點打上"正在匹配"的標記,並設定為 head
else if (casHead(h, s=snode(s, e, h, FULFILLING|mode))) {
// 迴圈loop
for (;;) {
// s為當前節點,m是s的next節點,
// m節點是s節點的匹配節點
SNode m = s.next;
// m == null,其他節點把m節點匹配走了
if (m == null) {
// 將s彈出
casHead(s, null);
// 將s置空,下輪迴圈的時候還會新建
s = null;
// 退出該迴圈,繼續主迴圈
break;
}
// 獲取m的next節點
SNode mn = m.next;
// 嘗試匹配
if (m.tryMatch(s)) {
// 匹配成功,將s 、 m彈出
casHead(s, mn); // pop both s and m
return (E) ((mode == REQUEST) ? m.item : s.item);
} else
// 如果沒有匹配成功,說明有其他執行緒已經匹配了,把m移出
s.casNext(m, mn);
}
}
}
// 到這最後一步說明節點正在匹配階段
else {
// head 的next的節點,是正在匹配的節點,m 和 h配對
SNode m = h.next;
// m == null 其他執行緒把m節點搶走了,彈出h節點
if (m == null)
casHead(h, null);
else {
SNode mn = m.next;
if (m.tryMatch(h))
casHead(h, mn);
else
h.casNext(m, mn);
}
}
}
}
SNode awaitFulfill(SNode s, boolean timed, long nanos) {
// 超時
final long deadline = timed ? System.nanoTime() + nanos : 0L;
// 當前執行緒
Thread w = Thread.currentThread();
// 自旋次數
// shouldSpin 用於檢測當前節點是否需要自旋
// 如果棧為空、該節點是首節點或者該節點是匹配節點,則先採用自旋,否則阻塞
int spins = (shouldSpin(s) ?
(timed ? maxTimedSpins : maxUntimedSpins) : 0);
for (;;) {
// 執行緒中斷了,取消該節點
if (w.isInterrupted())
s.tryCancel();
// 匹配節點
SNode m = s.match;
// 如果匹配節點m不為空,則表示匹配成功,直接返回
if (m != null)
return m;
// 超時
if (timed) {
nanos = deadline - System.nanoTime();
// 節點超時,取消
if (nanos <= 0L) {
s.tryCancel();
continue;
}
}
// 自旋;每次自旋的時候都需要檢查自身是否滿足自旋條件,滿足就 - 1,否則為0
if (spins > 0)
spins = shouldSpin(s) ? (spins-1) : 0;
// 第一次阻塞時,會將當前執行緒設定到s上
else if (s.waiter == null)
s.waiter = w;
// 阻塞 當前執行緒
else if (!timed)
LockSupport.park(this);
// 超時
else if (nanos > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanos);
}
}
相關文章
- Java併發包原始碼學習系列:阻塞佇列實現之SynchronousQueue原始碼解析Java原始碼佇列
- 阻塞佇列 LinkedTransferQueue 原始碼解析佇列原始碼
- 阻塞佇列 DelayQueue 原始碼解析佇列原始碼
- 阻塞佇列 PriorityBlockingQueue 原始碼解析佇列BloC原始碼
- Laravel 佇列原始碼解析Laravel佇列原始碼
- Java併發包原始碼學習系列:阻塞佇列實現之ArrayBlockingQueue原始碼解析Java原始碼佇列BloC
- Java併發包原始碼學習系列:阻塞佇列實現之LinkedBlockingQueue原始碼解析Java原始碼佇列BloC
- Java併發包原始碼學習系列:阻塞佇列實現之PriorityBlockingQueue原始碼解析Java原始碼佇列BloC
- Java併發包原始碼學習系列:阻塞佇列實現之LinkedBlockingDeque原始碼解析Java原始碼佇列BloC
- Java併發包原始碼學習系列:阻塞佇列實現之LinkedTransferQueue原始碼解析Java原始碼佇列
- Java併發包原始碼學習系列:阻塞佇列實現之DelayQueue原始碼解析Java原始碼佇列
- Java阻塞佇列中的異類,SynchronousQueue底層實現原理剖析Java佇列
- 佇列、阻塞佇列佇列
- 阻塞佇列一——java中的阻塞佇列佇列Java
- 探討阻塞佇列和執行緒池原始碼佇列執行緒原始碼
- 原始碼剖析ThreadPoolExecutor執行緒池及阻塞佇列原始碼thread執行緒佇列
- Java併發包原始碼學習系列:基於CAS非阻塞併發佇列ConcurrentLinkedQueue原始碼解析Java原始碼佇列
- 從原始碼解析-Android資料結構之單向阻塞佇列LinkedBlockingQueue的使用原始碼Android資料結構佇列BloC
- 迴歸Java基礎:LinkedBlockingQueue阻塞佇列解析JavaBloC佇列
- 阻塞佇列 BlockingQueue佇列BloC
- 阻塞佇列--LinkedBlockingQueue佇列BloC
- 死磕阻塞佇列佇列
- [原始碼解析] 訊息佇列 Kombu 之 基本架構原始碼佇列架構
- 多執行緒程式設計-分析阻塞佇列的原始碼實現執行緒程式設計佇列原始碼
- 延遲阻塞佇列 DelayQueue佇列
- 阻塞佇列BlockingQueue(三)--DelayQueue佇列BloC
- Java中的阻塞佇列Java佇列
- 阻塞佇列——四組API佇列API
- 原始碼解析Synchronous Queue 這種特立獨行的佇列原始碼佇列
- AQS原始碼深入分析之條件佇列-你知道Java中的阻塞佇列是如何實現的嗎?AQS原始碼佇列Java
- 聊聊併發(四)——阻塞佇列佇列
- Java中常用的七個阻塞佇列第二篇DelayQueue原始碼介紹Java佇列原始碼
- Java併發包原始碼學習系列:阻塞佇列BlockingQueue及實現原理分析Java原始碼佇列BloC
- laravel原始碼分析-佇列QueueLaravel原始碼佇列
- [原始碼解析] 分散式任務佇列 Celery 之啟動 Consumer原始碼分散式佇列
- 阻塞佇列 BlockingQueue(二)--ArrayBlockingQueue與LinkedBlockingQueue佇列BloC
- Java併發系列 — 阻塞佇列(BlockingQueue)Java佇列BloC
- Java併發——阻塞佇列集(上)Java佇列