問題
どのような(1)フェイザー?
(2)フェイザーどのような機能を?
(3)フェイザーCyclicBarrierをそしてたCountDownLatchの相対的な利点?
簡単な紹介
ステージに翻訳されたフェイザーは、それがこのようなシナリオに適用され、大規模なタスクが完了するまでにいくつかの段階に分けることができ、かつミッションの各フェーズは、複数のスレッドが同時に実行かもしれないが、彼らができる前の段階にあることが必要なタスクが完了していますミッションの次の段階を実行します。
このシナリオがCyclicBarrierをまたはCountryDownLatchも達成することが、はるかに複雑ことができます。まず、ステージ数の具体的なニーズがなりやすい、そして第二に、各段階でのタスクの数も気まぐれかもしれません。CyclicBarrierをしてたCountDownLatch、フェイザーより柔軟でより便利に比べて。
使用
のは、簡単なユースケースを見てみましょう:
public class PhaserTest {
public static final int PARTIES = 3;
public static final int PHASES = 4;
public static void main(String[] args) {
Phaser phaser = new Phaser(PARTIES) {
@Override
protected boolean onAdvance(int phase, int registeredParties) {
// 【本篇文章由公众号“彤哥读源码”原创,请支持原创,谢谢!】
System.out.println("=======phase: " + phase + " finished=============");
return super.onAdvance(phase, registeredParties);
}
};
for (int i = 0; i < PARTIES; i++) {
new Thread(()->{
for (int j = 0; j < PHASES; j++) {
System.out.println(String.format("%s: phase: %d", Thread.currentThread().getName(), j));
phaser.arriveAndAwaitAdvance();
}
}, "Thread " + i).start();
}
}
}
ここでは、大規模なタスクを完了するの四つの段階を必要と定義し、各フェーズは、これらのタスクのために、我々は、これらのタスクを実行するために3つのスレッドを開始し、出力結果を表示して、三つの小さなタスクが必要です。
Thread 0: phase: 0
Thread 2: phase: 0
Thread 1: phase: 0
=======phase: 0 finished=============
Thread 2: phase: 1
Thread 0: phase: 1
Thread 1: phase: 1
=======phase: 1 finished=============
Thread 1: phase: 2
Thread 0: phase: 2
Thread 2: phase: 2
=======phase: 2 finished=============
Thread 0: phase: 3
Thread 2: phase: 3
Thread 1: phase: 3
=======phase: 3 finished=============
私たちは、各ステージは3つのスレッドは、次の段階に入る前に完了しているされていることがわかります。これは、私たちはそれを学ぶために協力しましょう、それを達成する方法です。
原則推測
私たちの以前の研究AQSの原則に従えば、おそらく原則フェイザーの実現を推測します。
まず、あなたは、現在のタスクの数(参加者)当事者の位相は、参加者の数が完了していない、我々は状態変数に保存されているこれらの三つの変数を置くことができ、位相の現在のステージを保存する必要があります。
第二に、最後の参加者がタスクを完了すると、完了するために、参加者のキューを格納する必要が、参加者はキューに目を覚ますする必要があります。
ああ、このようなほとんど。
上記のケースでは:
現在の初期段階は、参加者の数が、参加者の数が3を完了していない3であり、ゼロです。
最初のスレッドの実行phaser.arriveAndAwaitAdvance();
キューに入ります。
2番目のスレッドの実行phaser.arriveAndAwaitAdvance();
キューに入ります。
実行するための第三のスレッドphaser.arriveAndAwaitAdvance();
このエグゼクティブ・サマリーの時に第一段階をonAdvance()
、その後、ミッションの次の段階に進み最初の二つのスレッドを覚まします。
ああ、全体がそうでないと、私たちはそれのソースを見てみましょう、意味をなすことができます。
ソースコード解析
主な内部クラス
static final class QNode implements ForkJoinPool.ManagedBlocker {
final Phaser phaser;
final int phase;
final boolean interruptible;
final boolean timed;
boolean wasInterrupted;
long nanos;
final long deadline;
volatile Thread thread; // nulled to cancel wait
QNode next;
QNode(Phaser phaser, int phase, boolean interruptible,
boolean timed, long nanos) {
this.phaser = phaser;
this.phase = phase;
this.interruptible = interruptible;
this.nanos = nanos;
this.timed = timed;
this.deadline = timed ? System.nanoTime() + nanos : 0L;
thread = Thread.currentThread();
}
}
ノードを完了するために、キュー内の参加者は、ここで我々だけに集中する必要があるthread
とnext
二つの性質があることができ、単一リンクリストで、スレッドがチームの中に保存されていることは明らかです。
主なプロパティ
// 状态变量,用于存储当前阶段phase、参与者数parties、未完成的参与者数unarrived_count
private volatile long state;
// 最多可以有多少个参与者,即每个阶段最多有多少个任务
private static final int MAX_PARTIES = 0xffff;
// 最多可以有多少阶段
private static final int MAX_PHASE = Integer.MAX_VALUE;
// 参与者数量的偏移量
private static final int PARTIES_SHIFT = 16;
// 当前阶段的偏移量
private static final int PHASE_SHIFT = 32;
// 未完成的参与者数的掩码,低16位
private static final int UNARRIVED_MASK = 0xffff; // to mask ints
// 参与者数,中间16位
private static final long PARTIES_MASK = 0xffff0000L; // to mask longs
// counts的掩码,counts等于参与者数和未完成的参与者数的'|'操作
private static final long COUNTS_MASK = 0xffffffffL;
private static final long TERMINATION_BIT = 1L << 63;
// 一次一个参与者完成
private static final int ONE_ARRIVAL = 1;
// 增加减少参与者时使用
private static final int ONE_PARTY = 1 << PARTIES_SHIFT;
// 减少参与者时使用
private static final int ONE_DEREGISTER = ONE_ARRIVAL|ONE_PARTY;
// 没有参与者时使用
private static final int EMPTY = 1;
// 用于求未完成参与者数量
private static int unarrivedOf(long s) {
int counts = (int)s;
return (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK);
}
// 用于求参与者数量(中间16位),注意int的位置
private static int partiesOf(long s) {
return (int)s >>> PARTIES_SHIFT;
}
// 用于求阶段数(高32位),注意int的位置
private static int phaseOf(long s) {
return (int)(s >>> PHASE_SHIFT);
}
// 已完成参与者的数量
private static int arrivedOf(long s) {
int counts = (int)s; // 低32位
return (counts == EMPTY) ? 0 :
(counts >>> PARTIES_SHIFT) - (counts & UNARRIVED_MASK);
}
// 用于存储已完成参与者所在的线程,根据当前阶段的奇偶性选择不同的队列
private final AtomicReference<QNode> evenQ;
private final AtomicReference<QNode> oddQ;
主な財産state
とevenQ
とoddQ
:
(1)状態、状態変数、電流位相の位相を格納するための上位32ビットは、中間記憶装置16の参加者数は、参加者の数の下位16ビットは公衆によって完成されていない格納[この記事番号、オリジナルの「兄弟は、ソーストンを読みます」オリジナルをサポートしてください、ありがとうございました!];
(2)evenQとoddQは、参加者が最後の参加者がキューきっかけに、参加者は、ミッションの次の段階、またはミッションの最後に進んでタスクを完了するとき、ストアキューを完了しました。
コンストラクタ
public Phaser() {
this(null, 0);
}
public Phaser(int parties) {
this(null, parties);
}
public Phaser(Phaser parent) {
this(parent, 0);
}
public Phaser(Phaser parent, int parties) {
if (parties >>> PARTIES_SHIFT != 0)
throw new IllegalArgumentException("Illegal number of parties");
int phase = 0;
this.parent = parent;
if (parent != null) {
final Phaser root = parent.root;
this.root = root;
this.evenQ = root.evenQ;
this.oddQ = root.oddQ;
if (parties != 0)
phase = parent.doRegister(1);
}
else {
this.root = this;
this.evenQ = new AtomicReference<QNode>();
this.oddQ = new AtomicReference<QNode>();
}
// 状态变量state的存储分为三段
this.state = (parties == 0) ? (long)EMPTY :
((long)phase << PHASE_SHIFT) |
((long)parties << PARTIES_SHIFT) |
((long)parties);
}
そこコンストラクタ親もあり、マルチレベルの段階を構築するために使用されるルートは、本稿の範囲を超えて、それは無視されます。
焦点32は、現在のステージ相高い、方法状態の割り当てを参照することであり、参加者の数を記憶するための中間記憶装置16人の参加者の数、下位16ビットは、完全ではありません。
ここでは、いくつかの主要な方法のソースを見てみましょう:
()メソッドを登録
メソッドが呼び出された場合、onAdvance()メソッドが実行された登録者は、この方法は、その完成を待ちます。
public int register() {
return doRegister(1);
}
private int doRegister(int registrations) {
// state应该加的值,注意这里是相当于同时增加parties和unarrived
long adjust = ((long)registrations << PARTIES_SHIFT) | registrations;
final Phaser parent = this.parent;
int phase;
for (;;) {
// state的值
long s = (parent == null) ? state : reconcileState();
// state的低32位,也就是parties和unarrived的值
int counts = (int)s;
// parties的值
int parties = counts >>> PARTIES_SHIFT;
// unarrived的值
int unarrived = counts & UNARRIVED_MASK;
// 检查是否溢出
if (registrations > MAX_PARTIES - parties)
throw new IllegalStateException(badRegister(s));
// 当前阶段phase
phase = (int)(s >>> PHASE_SHIFT);
if (phase < 0)
break;
// 不是第一个参与者
if (counts != EMPTY) { // not 1st registration
if (parent == null || reconcileState() == s) {
// unarrived等于0说明当前阶段正在执行onAdvance()方法,等待其执行完毕
if (unarrived == 0) // wait out advance
root.internalAwaitAdvance(phase, null);
// 否则就修改state的值,增加adjust,如果成功就跳出循环
else if (UNSAFE.compareAndSwapLong(this, stateOffset,
s, s + adjust))
break;
}
}
// 是第一个参与者
else if (parent == null) { // 1st root registration
// 计算state的值
long next = ((long)phase << PHASE_SHIFT) | adjust;
// 修改state的值,如果成功就跳出循环
if (UNSAFE.compareAndSwapLong(this, stateOffset, s, next))
break;
}
else {
// 多层级阶段的处理方式
synchronized (this) { // 1st sub registration
if (state == s) { // recheck under lock
phase = parent.doRegister(1);
if (phase < 0)
break;
// finish registration whenever parent registration
// succeeded, even when racing with termination,
// since these are part of the same "transaction".
while (!UNSAFE.compareAndSwapLong
(this, stateOffset, s,
((long)phase << PHASE_SHIFT) | adjust)) {
s = state;
phase = (int)(root.state >>> PHASE_SHIFT);
// assert (int)s == EMPTY;
}
break;
}
}
}
}
return phase;
}
// 等待onAdvance()方法执行完毕
// 原理是先自旋一定次数,如果进入下一个阶段,这个方法直接就返回了,
// 如果自旋一定次数后还没有进入下一个阶段,则当前线程入队列,等待onAdvance()执行完毕唤醒
private int internalAwaitAdvance(int phase, QNode node) {
// 保证队列为空
releaseWaiters(phase-1); // ensure old queue clean
boolean queued = false; // true when node is enqueued
int lastUnarrived = 0; // to increase spins upon change
// 自旋的次数
int spins = SPINS_PER_ARRIVAL;
long s;
int p;
// 检查当前阶段是否变化,如果变化了说明进入下一个阶段了,这时候就没有必要自旋了
while ((p = (int)((s = state) >>> PHASE_SHIFT)) == phase) {
// 如果node为空,注册的时候传入的为空
if (node == null) { // spinning in noninterruptible mode
// 未完成的参与者数量
int unarrived = (int)s & UNARRIVED_MASK;
// unarrived有变化,增加自旋次数
if (unarrived != lastUnarrived &&
(lastUnarrived = unarrived) < NCPU)
spins += SPINS_PER_ARRIVAL;
boolean interrupted = Thread.interrupted();
// 自旋次数完了,则新建一个节点
if (interrupted || --spins < 0) { // need node to record intr
node = new QNode(this, phase, false, false, 0L);
node.wasInterrupted = interrupted;
}
}
else if (node.isReleasable()) // done or aborted
break;
else if (!queued) { // push onto queue
// 节点入队列
AtomicReference<QNode> head = (phase & 1) == 0 ? evenQ : oddQ;
QNode q = node.next = head.get();
if ((q == null || q.phase == phase) &&
(int)(state >>> PHASE_SHIFT) == phase) // avoid stale enq
queued = head.compareAndSet(q, node);
}
else {
try {
// 当前线程进入阻塞状态,跟调用LockSupport.park()一样,等待被唤醒
ForkJoinPool.managedBlock(node);
} catch (InterruptedException ie) {
node.wasInterrupted = true;
}
}
}
// 到这里说明节点所在线程已经被唤醒了
if (node != null) {
// 置空节点中的线程
if (node.thread != null)
node.thread = null; // avoid need for unpark()
if (node.wasInterrupted && !node.interruptible)
Thread.currentThread().interrupt();
if (p == phase && (p = (int)(state >>> PHASE_SHIFT)) == phase)
return abortWait(phase); // possibly clean up on abort
}
// 唤醒当前阶段阻塞着的线程
releaseWaiters(phase);
return p;
}
参加者の全体的なロジックを増やします。
(1)参加者を追加し、2つの当事者、すなわち状態16と下位16ビットに、未着値を大きくする必要があります。
参加者が最初である場合(2)、次いで、正常終了ならば、原子の状態の値を更新しよう。
参加者が初めてではない場合はそうでない場合は正常終了するまで、原子状態の値を更新しようとした場合(3)、その後、実装onAdvance()をチェックし、それがonAdvanceを待っている場合は()の実行は、中に完了していません。
(4)onAdvance()を切り替えるスレッドコンテキストを低減する、待ち行列待ちにスピンの第一の実施形態を使用して完了した待ち。
arriveAndAwaitAdvance()メソッド
現在のスレッドの現在のフェーズは、現在の段階を完了するために他のスレッドを待って、終了します。
現在のスレッドがこの段階に到達する最後のものである場合、現在のスレッドがonAdvance()メソッドを実行し、次のフェーズに他のスレッドを起動します。
public int arriveAndAwaitAdvance() {
// Specialization of doArrive+awaitAdvance eliminating some reads/paths
final Phaser root = this.root;
for (;;) {
// state的值
long s = (root == this) ? state : reconcileState();
// 当前阶段
int phase = (int)(s >>> PHASE_SHIFT);
if (phase < 0)
return phase;
// parties和unarrived的值
int counts = (int)s;
// unarrived的值(state的低16位)
int unarrived = (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK);
if (unarrived <= 0)
throw new IllegalStateException(badArrive(s));
// 修改state的值
if (UNSAFE.compareAndSwapLong(this, stateOffset, s,
s -= ONE_ARRIVAL)) {
// 如果不是最后一个到达的,则调用internalAwaitAdvance()方法自旋或进入队列等待
if (unarrived > 1)
// 这里是直接返回了,internalAwaitAdvance()方法的源码见register()方法解析
return root.internalAwaitAdvance(phase, null);
// 到这里说明是最后一个到达的参与者
if (root != this)
return parent.arriveAndAwaitAdvance();
// n只保留了state中parties的部分,也就是中16位
long n = s & PARTIES_MASK; // base of next state
// parties的值,即下一次需要到达的参与者数量
int nextUnarrived = (int)n >>> PARTIES_SHIFT;
// 执行onAdvance()方法,返回true表示下一阶段参与者数量为0了,也就是结束了
if (onAdvance(phase, nextUnarrived))
n |= TERMINATION_BIT;
else if (nextUnarrived == 0)
n |= EMPTY;
else
// n 加上unarrived的值
n |= nextUnarrived;
// 下一个阶段等待当前阶段加1
int nextPhase = (phase + 1) & MAX_PHASE;
// n 加上下一阶段的值
n |= (long)nextPhase << PHASE_SHIFT;
// 修改state的值为n
if (!UNSAFE.compareAndSwapLong(this, stateOffset, s, n))
return (int)(state >>> PHASE_SHIFT); // terminated
// 唤醒其它参与者并进入下一个阶段
releaseWaiters(phase);
// 返回下一阶段的值
return nextPhase;
}
}
}
ロジックarriveAndAwaitAdvanceは、実質的に次のように:
(1)未着マイナス1状態部分の値を変更します。
(2)それが到着する最後のない場合、コールinternalAwaitAdvance()メソッド、またはスピン待機します。
(3)それが到着する最後である場合、コールonAdvance()メソッド、およびその後の状態に対応する次の位相値の値を変更し、他のを待っているスレッドをウェイクアップ。
(4)次の段の値を返します。
概要
(1)多段マルチタスクのシナリオに適用フェイザー、各フェーズのタスクを細かく制御することができます。
内部(2)フェイザーキュー状態変数を使用して、パブリック[いいえ、元の「トンの弟は、ソースを読む」ことで、この記事の全体のロジックを実現、オリジナルをサポートしてください、ありがとうございました!];
(3)高い32ビットの記憶相の電流位相の状態、参加者の数の当事者16は、現在の位相(タスク)、参加者の下位16ビットを格納未完成未着番号。
(4)キューの現在のステージとは異なるパリティに基づいてキューを選択します。
(5)参加者が到着した最後の時間でない場合は、キューイング入力するか、タスクを完了するためにすべての参加をお待ちしていますスピン。
最後に参加者がタスクを完了する場合には(6)、キューのスレッドで目を覚ますと、次のステージに移動します。
卵
フェイザーCyclicBarrierをしてたCountDownLatchの相対的な利点は?
:2つの主な利点があります。
(1)フェイザー完全多段階、及び制御CyclicBarrierをあるいはたCountDownLatchタスクの一般1つまたは2つのステージを、
それが決定されると、(2)それぞれの位相を制御することができるタスクフェイザーの数、およびタスクたCountDownLatch CyclicBarrierを数または変更することはできません。
推奨読書
3、JMMのサイケのJava同期シリーズ(Javaのメモリモデル)
8、公正ロック不公平ロック-サイケReentrantLockのは、(A)のJavaソースコード同期シリーズを解決します
9、条件ロック- (2)のサイケReentrantLockの解決Javaソースコード同期シリーズ
10、ReentrantLockのVSのサイケのJava同期シリーズ同期
11、細流ReentrantReadWriteLock解析Javaソースシンクロナスシリーズ
13、同期直列たCountDownLatchを解析細流のJavaソース
15、細流StampedLock解析Javaソースシンクロナスシリーズ
16、サイケのJavaソースシンクロナスシリーズは解決CyclicBarrierを
[この記事オリジナルの「トンの弟は、ソースを読む」から、オリジナルをサポートしてください、ありがとうございました!]