キューシリーズのブロック:
- [JUCソースコード]ブロッキングキュー:継承関係とインターフェース分析
- [JUCソースコード]ブロッキングキュー:ArrayBlockingQueueソースコード分析
- [JUCソースコード]ブロッキングキュー:LinkedBlockingQueueソースコード分析
- [JUCソースコード]ブロッキングキュー:DelayQueueソースコード分析
- [JUCソースコード]ブロッキングキュー:SynchronousQueueソースコード分析(不公平モード)
- [JUCソースコード]ブロッキングキュー:SynchronousQueueソースコード分析(フェアモード)
- [JUCソースコード]ブロッキングキュー:概要-機能の比較とアプリケーションシナリオの例
- [JUCソースコード]ブロッキングキュー:さまざまなブロッキングキューのインタビュー質問のコレクション
では、前の記事では、我々はスタック(FILO)に基づいてTransferStackの不公平なスレッド制御ロジックについて話しました。この記事では、我々はキュー(FIFO)に基づいてTransferQueueの公正なスレッド制御ロジックを見ていきます。
最初に質問を確認しましょう。現在のスレッドは、ブロックされたスレッドに独自のデータをどのように渡しますか?
説明の便宜上、スレッド1がデータをキューに取り込み、ブロックされてブロックされたスレッドAになり、スレッド2がデータBをキューに入れ始めると仮定します。一般的なフローは次のとおりです。
- スレッド1はキューからデータを取得し、キューにデータがないことを検出したため、ブロックされてAになります。
- スレッド2はデータをキューの最後に置き、キューの最後から最初にブロックされたノードを見つけます。この時点でノードAが見つかると仮定すると、スレッドBは入れたデータをノードAのitem属性に入れます。スレッド1をウェイクアップします。
- スレッド1が起動した後、スレッド2 putのデータをA.itemから取得でき、スレッド1は正常に戻ります。
このプロセスから、公平性は主に、データが配置されるたびにチームの最後に配置され、データが取得されるたびにデータがヘッドから直接取得されないという事実に反映されていることがわかります。チームですが、チームの最後からです。最初にブロックされたスレッドを探して、ブロックされたスレッドが順番に解放されるようにします。
PS:TransferStackのスレッドマッチングは、スタックの最上位に近いスタックのマッチングに基づいているため、公平ではありません。
TransferQueue(Fair => FIFO)
static final class TransferQueue<E> extends Transferer<E>{
// 队列头
transient volatile QNode head;
// 队列尾
transient volatile QNode tail;
// 队列的元素
static final class QNode {
...}
//...
}
QNode
static final class QNode {
// 当前元素的值,如果当前元素被阻塞住了,等其他线程来唤醒自己时,其他线程会把自己 set 到 item 里面
volatile Object item; // CAS'ed to or from null
// 可以阻塞住的当前线程
volatile Thread waiter; // to control park/unpark
// true 是 put,false 是 take
final boolean isData;
// 当前元素的下一个元素
volatile QNode next;
// 构造时传入数据和节点类型
QNode(Object item, boolean isData) {
this.item = item;
this.isData = isData;
}
// 将节点val通过cas连接在cmp后面
boolean casNext(QNode cmp, QNode val) {
return next == cmp &&
UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
}
// 将item的值通过cas变为val
boolean casItem(Object cmp, Object val) {
return item == cmp &&
UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
}
/**
* Tries to cancel by CAS'ing ref to this as item.
*/
void tryCancel(Object cmp) {
UNSAFE.compareAndSwapObject(this, itemOffset, cmp, this);
}
// 被取消的节点就是item=this
boolean isCancelled() {
return item == this;
}
// unsafe相关代码...
}
transfer():エンキューとデキュー
E transfer(E e, boolean timed, long nanos) {
QNode s = null; // constructed/reused as needed
// true 是 put,false 是 get
boolean isData = (e != null);
for (;;) {
// 队列头和尾的临时变量,队列是空的时候,t=h
QNode t = tail;
QNode h = head;
// tail 和 head 没有初始化时,无限循环
// 虽然这种continue非常耗cpu,但一般碰不到这种情况,因为tail和head 在 TransferQueue 初始化时就已经被赋值空节点了
if (t == null || h == null)
continue;
-----------------------------------------------------------------------------------------------------------------
// 情况一:首尾节点相同(空队列)|| 尾节点的操作和当前节点操作一致(比如队尾是take时阻塞,当前线程也是take)
if (h == t || t.isData == isData) {
QNode tn = t.next;
// 当 t 不是 tail 时。即 tail 已经被修改过了,因为 tail 没有被修改的情况下,t 和 tail 必然相等
if (t != tail)
continue;
// 队尾后面的值还不为空,t 还不是队尾,直接把 tn 赋值给 t,这是一步加强校验。
if (tn != null) {
// CAS修改tail为tn
advanceTail(t, tn);
continue;
}
// 超时直接返回 null
if (timed && nanos <= 0) // can't wait
return null;
// 构造node节点
if (s == null)
s = new QNode(e, isData);
// 如果把 e 放到队尾失败,继续递归放进去
if (!t.casNext(null, s)) // failed to link in
continue;
advanceTail(t, s); // swing tail and wait
// awaitFulfill 同 TransferStack,阻塞住自己,等待配对节点x
Object x = awaitFulfill(s, e, timed, nanos);
if (x == s) {
// wait was cancelled
clean(t, s);
return null;
}
if (!s.isOffList()) {
// not already unlinked
// CAS修改head为s
advanceHead(t, s); // unlink if head
if (x != null) // and forget fields
s.item = s;
s.waiter = null;
}
return (x != null) ? (E)x : e;
-----------------------------------------------------------------------------------------------------------------
// 情况二:队列不为空,并且当前操作和队尾不一致(比如队尾是因为 take 被阻塞的,那么当前操作必然是 put)
} else {
// complementary-mode
// 如果是第一次执行,此处的 m 代表就是 tail
// 也就是这行代码体现出队列的公平,每次操作时,从头开始按照顺序进行操作
QNode m = h.next; // node to fulfill
if (t != tail || m == null || h != head)
continue; // inconsistent read
Object x = m.item;
if (isData == (x != null) || // m already fulfilled
x == m || // m cancelled
// m 代表栈头
// 这里把当前的操作值赋值给阻塞住的 m 的 item 属性,所以 m 被释放时,就可得到此次操作的值
!m.casItem(x, e)) {
// lost CAS
advanceHead(h, m); // dequeue and retry
continue;
}
// 当前操作放到队头
advanceHead(h, m); // successfully fulfilled
// 释放队头阻塞节点
LockSupport.unpark(m.waiter);
return (x != null) ? (E)x : e;
}
}
}