[TOC]
SynchronousQueue 1.8ソースコードの解析
I.はじめに
SynchronousQueueは、キューを呼び出していないいない内部データ・ストレージ・スペースので、キューは、覗き見することができない要素が存在しないため、スレッドはいずれかのキューがあるチームにスレッドまでキューに入れられたブロックします感じて、非常に奇妙なキューです手をつないで行く必要があり、エンキューを待つ必要があり、チームが同じである、一連の操作は、エンキューおよびデキューが一緒に残してあり、公正かつ不当なモードのためのキュー(スレッドを一致するキューを参照してください)、公正モードデータ構造は、非公正なモードがスタック(LIFO)で使用されるキュー(FIFO)です。
二、UMLダイアグラム
第三に、基本的なメンバー
abstract static class Transferer<E> {
// 出队入队都是这一个方法
abstract E transfer(E e, boolean timed, long nanos);
}
// npu数
static final int NCPUS = Runtime.getRuntime().availableProcessors();
// 带超时时间的自旋次数
static final int maxTimedSpins = (NCPUS < 2) ? 0 : 32;
// 没有超时的自旋次数
static final int maxUntimedSpins = maxTimedSpins * 16;
不当な実装TransferStack、主要メンバー
TransferStackは、転写手段を継承しました
注意:転写手段を継承しているため、チームとチームからの転送方法が使用されているように、これらの状態は、重要であり、状態は区別するために使用され、方法のセクションでは、後で説明します
/** 0表示消费者 */
static final int REQUEST = 0;
/** 1表示数据的生产者 */
static final int DATA = 1;
/** 2 表示数据正在匹配 */
static final int FULFILLING = 2;
static final class SNode {
volatile SNode next; // 下一个节点
volatile SNode match; // 匹配的节点
volatile Thread waiter; // 等待的线程
Object item; // 数据
int mode; // 模式 0 , 1 , 2
}
/** 头结点 */
volatile SNode head;
TransferQueue公正な実施、主要メンバー
TransferQueueは、転写手段を継承しました
static final class QNode {
volatile QNode next; // next 节点
volatile Object item; // 数据项
volatile Thread waiter; // 等待线程
final boolean isData; // 区分生产和消费
}
/** 头结点 */
transient volatile QNode head;
/** 尾节点 */
transient volatile QNode tail;
第四に、一般的な方法
コンストラクタ
public SynchronousQueue() {
this(false);
}
// 构造方法,fair表示公平或者非公平
public SynchronousQueue(boolean fair) {
transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
}
TransferStack不当な一般的な方法
提供方法
public boolean offer(E e) {
// e 不能为null
if (e == null) throw new NullPointerException();
return transferer.transfer(e, true, 0) != null;
}
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
// e 不能为null
if (e == null) throw new NullPointerException();
if (transferer.transfer(e, true, unit.toNanos(timeout)) != null)
return true;
if (!Thread.interrupted())
return false;
throw new InterruptedException();
}
putメソッド
public void put(E e) throws InterruptedException {
// e 不能为null
if (e == null) throw new NullPointerException();
if (transferer.transfer(e, false, 0) == null) {
// 支持中断
Thread.interrupted();
throw new InterruptedException();
}
投票方法
public E poll() {
return transferer.transfer(null, true, 0);
}
take 方法
public E take() throws InterruptedException {
E e = transferer.transfer(null, false, 0);
if (e != null)
return e;
Thread.interrupted();
throw new InterruptedException();
}
転送方法
E transfer(E e, boolean timed, long nanos) {
SNode s = null; // constructed/reused as needed
// 根据所传元素判断为生产or消费
int mode = (e == null) ? REQUEST : DATA;
for (;;) { // 无限循环
SNode h = head; // 获取头结点
if (h == null || h.mode == mode) { // 头结点为空或者当前节点状态(0,1,2)和头结点相同
if (timed && nanos <= 0) { // can't wait 设置有时间
// 节点不为null并且为取消状态
if (h != null && h.isCancelled())
// 弹出取消的节点
casHead(h, h.next); // pop cancelled node
else
// 超时直接返回null
return null;
// 没有设置超时
} else if (casHead(h, s = snode(s, e, h, mode))) { // 将h设为自己的next节点
// 空旋或者阻塞直到s结点被FulFill操作所匹配
SNode m = awaitFulfill(s, timed, nanos);
if (m == s) { // wait was cancelled 节点被取消了
clean(s);
return null;
}
// 找到匹配的线程了
// h == head 可能已经已经被匹配
// h.next 等于s 不同类型
if ((h = head) != null && h.next == s)
// 弹出h 和 s
casHead(h, s.next); // help s's fulfiller
return (E) ((mode == REQUEST) ? m.item : s.item);
}
// 未匹配
} else if (!isFulfilling(h.mode)) { // try to fulfill // 尝试匹配节点
if (h.isCancelled()) // already cancelled // 节点被取消
casHead(h, h.next); // pop and retry // 修改头结点
else if (casHead(h, s=snode(s, e, h, FULFILLING|mode))) {
for (;;) { // loop until matched or waiters disappear
SNode m = s.next; // m is s's match
// 没有下一个节点了,结束这次循环,走最外层循环重新开始
if (m == null) { // all waiters are gone // m等于null
casHead(s, null); // pop fulfill node // cas 设置head
s = null; // use new node next time
break; // restart main loop // 结束循环
}
SNode mn = m.next;
if (m.tryMatch(s)) { // 尝试匹配,成功
casHead(s, mn); // pop both s and m
return (E) ((mode == REQUEST) ? m.item : s.item);
} else // lost match // 失败,说明m背的线程匹配了,或者取消了
s.casNext(m, mn); // help unlink // 修改next节点
}
}
} else { // help a fulfiller 正在匹配
SNode m = h.next; // m is h's match
if (m == null) // waiter is gone 匹配完成了
casHead(h, null); // pop fulfilling node
else {
SNode mn = m.next;
if (m.tryMatch(h)) // help match
casHead(h, mn); // pop both h and m
else // lost match
h.casNext(m, mn); // help unlink
}
}
}
}
awaitFulfill方法
SNode awaitFulfill(SNode s, boolean timed, long nanos) {
// 计算时间
final long deadline = timed ? System.nanoTime() + nanos : 0L;
// 获取当前线程
Thread w = Thread.currentThread();
// shouldSpin控制自旋
// shouldSpin 用于检测当前节点是否需要自旋
// 如果栈为空、该节点是首节点或者该节点是匹配节点,则先采用自旋,否则阻塞
int spins = (shouldSpin(s) ?
(timed ? maxTimedSpins : maxUntimedSpins) : 0);
for (;;) { // 死循环
if (w.isInterrupted()) // 线程被中断
s.tryCancel();
SNode m = s.match;
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;
// 设置node的线程
else if (s.waiter == null)
s.waiter = w; // establish waiter so can park next iter
// 如果不是超时,就阻塞
else if (!timed)
LockSupport.park(this);
// 设置超时阻塞
else if (nanos > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanos);
}
}
きれいな方法
void clean(SNode s) {
s.item = null; // forget item
s.waiter = null; // forget thread
// next节点
SNode past = s.next;
// next节点也被中断了,直接删除
if (past != null && past.isCancelled())
past = past.next;
// Absorb cancelled nodes at head
// 从栈顶开始找,清除取消的节点
SNode p;
while ((p = head) != null && p != past && p.isCancelled())
casHead(p, p.next);
// Unsplice embedded nodes
// 从有效的头节点开始p ,到s的后继节点,继续清除
while (p != null && p != past) {
SNode n = p.next;
if (n != null && n.isCancelled())
p.casNext(n, n.next);
else
p = n;
}
}
転送方法の分析:
-
私たちは、その転送が空またはポーリング方式はチームの中と外、それらを区別することです提供方法、であるeで判断することができました。
-
キューが空であるヘッド、又はヘッドノードのキューが存在し、同一のそれらのモードと、第1のケースが相殺最初に決定されるか、または、これらの操作の実装に動作をエンキューしていない実行され、新たに追加されたノード我々はパターンが同じで一致することはできませんので、スタックの先頭に参加し、それが中断されるまでスレッドawaitFulfillメソッドブロックを呼び出し、タイムアウトやマッチが成功すると、なぜ、ウェイク我々は唯一のスレッドまで試合の異なるモードをスレッドをブロックすることができ、詰まります自身、これは、プロセスawaitFulfill法の終わりです。
-
第二の場合は、チームの異なるモードならば、マッチング処理を実行しないように、マッチング方法をisFulfillingがヘッドノードによって決定されます
サイクルがこのような状況の上に、あなたはノードを一致させるために始めることができない場合は、プロセスを再起動する場合まず、ノードがキャンセルされているかどうかを決定し、その中に何の判断が一つのノードのみが、そこに彼がされていないではない、成功した別のノードをウェイクtryMatchメソッドを呼び出しますその後、スタックに沿って、結果を返し、競争一致他のスレッドが存在し得るノードを見つけるために、逆方向に進めるために、次のに失敗しました。
- 第三の場合、異なるパターンのチームに、しかし、ヘッドノードがマッチングされ、それが試合を完了するのに役立つだろうし、その後サイクル全体を再取ります。
TransferQueue共通持分法
チームとチームは、我々は、メインバーの転送方法を見て、同じ方法です。
転送方法
E transfer(E e, boolean timed, long nanos) {
QNode s = null; // constructed/reused as needed
// 判断是生产者 还是消费者
boolean isData = (e != null);
for (;;) {
QNode t = tail;
QNode h = head;
// 没有初始化
if (t == null || h == null) // saw uninitialized value
continue; // spin
// h==t 刚刚初始化 t.isData == isData,尾节点和当前节点的类型一样
if (h == t || t.isData == isData) { // empty or same-mode
QNode tn = t.next; // 获取尾节点
if (t != tail) // 尾节点发生变了变化 // inconsistent read
continue;
if (tn != null) { // 重设为节点 // lagging tail
advanceTail(t, tn);
continue;
}
if (timed && nanos <= 0) // 超时了 // can't wait
return null;
if (s == null) // 构建新节点
s = new QNode(e, isData);
if (!t.casNext(null, s)) // failed to link in 有竞争
continue;
// 替换尾节点
advanceTail(t, s); // swing tail and wait
// 自旋/阻塞 返回的是中断取消/匹配的节点
Object x = awaitFulfill(s, e, timed, nanos);
// 中断
if (x == s) { // wait was cancelled
clean(t, s);
return null;
}
// 匹配成功了,需要执行出队操作
if (!s.isOffList()) {
// not already unlinked
// 修改头结点
advanceHead(t, s); // unlink if head
if (x != null) // and forget fields
s.item = s;
s.waiter = null;
}
return (x != null) ? (E)x : e;
} else { // complementary-mode
// 出队从头元素开始
QNode m = h.next; // node to fulfill
if (t != tail || m == null || h != head) // 队列发生变化重来
continue; // inconsistent read
Object x = m.item;
// isData == (x != null) 判断模式是否相同,不相同才能匹配
// x == m 说明已经被中断或者超时了
// m.casItem(x, e) 匹配
if (isData == (x != null) || // m already fulfilled
x == m || // m cancelled
!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;
}
}
}
転送方法の分析:
- Eはまた、すべてのコール転送方式、転写法は、2つの部分、エンキューおよびマッチングに見ることができる、エンキューまたはデキューすることによって判定されます。
- チームの第1の部分は、チームモデルは、エンドノードが変更されていない決定、エンド・ノードを取得し、テール・ノードから開始同じエンキューされ、マルチスレッドが存在し得る、再反復する変化は、何尾を決定していませんノードは、ブレークするまで、現在のノードを遮断awaitFulfill呼び出して、それが新しいノードを追加し、別のスレッドがある示し、tailノードを更新する必要があり、何の次のノードを持っていない、し、現在の次のノードのエンド・ノードに参加する新しいノードを構築し、テール・ノードを更新しますアウトまたは一致し、成功する一致ノードをクリアし、きれいなメソッドを呼び出します。
- マッチ(チーム)の第2の部分は、チームは、ウェイクmはスレッドを待機スクラッチノードから起動し、一致が成功した場合、異なるモードは、キャンセルした場合、実際には、項目を設定CAS、データの転送であるか否かを決定することであり、ここで注意私たちが最初に一致し、次のノードのノードを取得するため、ヘッドノードmになるように設定、実際には、m個のノードは、ポップアップ表示します。
V.の概要
SynchronousQueue実装は非常に複雑であり、我々はブロッキングキューものの、しかし、ロックを使用していないことがわかります。配信シナリオのキューは、何のキューキュー記憶素子、チームと手をつないで行かなければならないチームはありません。
参考「アートのJava並行プログラミング。」