CyclicBarrier
同様のフェンスが開く前にフェンスが複数のオブジェクトの片側にブロックされる前に、削除されると同時に上方に移動することが開始されたときにフェンスにのみ、フェンスの片側にブロックすることができることを意味し、あります。
1. CyclicBarrierをを使用する方法
で、原則として、第一印象を導入する前にCyclicBarrier
、それがどのように使用しますか。
今、このようなAのシナリオがあることを、我々はオープンする必要があるとの会合を、あなたが必要とする張1、2張、張3 3人が、出席するために
3人を開始するために出席していた、または唯一のドライ待機した後、会議、このシーンはCyclicBarrier
非常にフィットすることができシミュレートされました。コードは以下の通りであります:
public static void main(String[] args) {
// 线程池,每个线程代表一个人
ThreadPoolExecutor executor = ThreadPoolProvider.getInstance();
// 会议所需的人数为3
CyclicBarrier barrier = new CyclicBarrier(3);
executor.execute(() -> {
try {
System.err.println("张1到达会议室");
barrier.await();
System.err.println("会议开始,张1开始发言");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
});
executor.execute(() -> {
try {
System.err.println("张2到达会议室");
barrier.await();
System.err.println("会议开始,张2开始发言");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
});
executor.execute(() -> {
try {
System.err.println("张3先去个厕所,内急解决再去开会");
TimeUnit.SECONDS.sleep(1);
System.err.println("张3到达会议室");
barrier.await();
System.err.println("会议开始,张3开始发言");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
});
executor.shutdown();
}
図は、結果:
コードの先頭を通じて知ることがCyclicBarrier
いくつかのポイントを:
- 使用する
await()
特定の物事の完了を示します。(例では、パフォーマンス上の会議室に達しました) - 使用
await()
後の現在のスレッドがブロックされた状態に入り、必要十分に満たすために待機するCyclicBarrier
ウェイクの条件を次の操作を継続することを。(上記の例では3人の会議室に達しています) - 開いているすべてのスレッドをブロックする前に条件を達成するために、最後のスレッド後に次の操作に進みます。(上記の例では、張リーチ3会議室です)
この単純な例では、私たちが知っているCyclicBarrier
方法を使用して、そしてそれは、フェンスの効果を達成するために正確にどのようにその内部を見ることです。
2. CyclicBarrierをどのようにの「フェンス」であります
私たちは2ヶ所に集中する必要があり、最初のコードから見ることができます
- コンストラクタ
- await()メソッド
限り、これら2つの方法は、内部を理解するように、理解に相当するCyclicBarrier
内部。
その掘り下げる前に、初めて目にCyclicBarrier
いくつかの変数、行にこの事をどのように処理するかを知るために、コードを見たときに、覚えておく必要はありません。
ロック:
CyclicBarrier
クラスが作成ReentrantLock
については、インスタンスをReentrantLock
不明瞭なことができます- > 転送。旅:
lock
でcondition
、CyclicBarrier
変数を使用して、ブロッキングを達成し、スレッド間で同時に目を覚まします。同様に、明確なcondition
アクション=> ポータル。当事者は:条件を満足させる必要が(呼び出し
await
、いくつかの方法を)、そしてそれは、スレッドが(は)すべてのスレッドをウェイクアップ待つパーティーがあるときです。barrierCommand:
Runnable
内の変数await
到着のメソッドの呼び出しの合計数parties
の後に、すべてのスレッドが目を覚ます前に、その実行run()
方法を世代:その内部クラスは、サイクルn個のタスクを完了し、期間として理解することができる限り、タスクが失敗したとして、障害があっても、現在の期間のタスクのすべての現在のサイクルを終了した後、次のサイクルを回します。
数:現在の期間行われる残りのタスクの数(残りのコール
await
番号方式)
以下がソースです。
public class CyclicBarrier {
// 内部类,可理解为周期
private static class Generation {
// 当前周期是否失败
boolean broken = false;
}
// 锁的实例
private final ReentrantLock lock = new ReentrantLock();
// ReentrantLock的condition变量,用来控制线程唤醒和阻塞
private final Condition trip = lock.newCondition();
// 需要满足条件的次数,即需要调用await方法的次数
private final int parties;
// 满足条件次数达到parties之后,唤醒所有线程之前执行其 run()方法
private final Runnable barrierCommand;
// 当前周期
private Generation generation = new Generation();
// 剩余满足条件次数
private int count;
// ...
}
読むCyclicBarrier
の内部実装の詳細を見て、いくつかの変数の後。
まず見た目のコンストラクタ 2つのコンストラクタ、持っている、すべてのスレッド(関係者)条件の合計数に達した後、直接ウェイクアップを、別の指定されたRunnable
最初の実行条件合計()メソッドに達した後に実行して、目を覚まします。
- 指定しないでください
Runnable
、パラメータを1つだけ:タスクの数に到達する必要性を
public CyclicBarrier(int parties) {
// 直接调用另一个构造方法,Runnable传null,表示不执行
this(parties, null);
}
- 指定
Runnable
コンストラクタ、タスクの割り当ての合計数、残りのタスクの数は、操作の前に目を覚ましますRunnable
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
// 任务总数
this.parties = parties;
// 剩余需要完成的任务数
this.count = parties;
// 唤醒之前执行的Runnable
this.barrierCommand = barrierAction;
}
最初のセクションでは、我々は、第二を試して、最初のコンストラクタを使用します
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor executor = ThreadPoolProvider.getInstance();
/** =======增加Runnable,其他地方保持一致=============*/
CyclicBarrier barrier = new CyclicBarrier(3, ()-> System.err.println("在会议开始之前,先给大家发下开会资料"));
executor.execute(() -> {
try {
System.err.println("张1到达会议室");
barrier.await();
System.err.println("会议开始,张1开始发言");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
});
executor.execute(() -> {
try {
System.err.println("张2到达会议室");
barrier.await();
System.err.println("会议开始,张2开始发言");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
});
executor.execute(() -> {
try {
System.err.println("张3先去个厕所,内急解决再去开会");
TimeUnit.SECONDS.sleep(1);
System.err.println("张3到达会议室");
barrier.await();
System.err.println("会议开始,张3开始发言");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
});
executor.shutdown();
}
図は、結果:
コンストラクタを読み取る、高く評価半分としてもCyclicBarrier
、次の用語の他の半分は- 、await()
トラッキングコードを、これが見られます
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
ダイレクトコールdowait
質量参加のための方法false
と0を意味し、スレッドが中断またはウェイクない限り、無制限の待機を。再進入dowait
である方法、CyclicBarrier
他の半分は、以下の明確全体の実行過程で書かれたコードに、
/** 参数说明, timed:是否限时, nanos:限时时间*/
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException, TimeoutException {
// 锁
final ReentrantLock lock = this.lock;
// 获取锁,如果失败的话线程睡眠,进入同步队列(AQS中的知识)
lock.lock();
try {
/* 拿到锁之后进入代码处理逻辑*/
// 当前周期
final Generation g = generation;
// 如果当前周期是失败的,那么直接抛错
if (g.broken)
throw new BrokenBarrierException();
// 如果当前线程被打断了,那么此次周期失败,设置相关参数,然后抛错
if (Thread.interrupted()) {
// 实现代码在下行的注释中,设置相关参数来提醒其他线程周期失败了
breakBarrier();
/*
* private void breakBarrier() {
* generation.broken = true;
* count = parties;
* // 唤醒condition中的所有线程
* trip.signalAll();
* }
*/
throw new InterruptedException();
}
// 如果成功了,那么剩余任务数(count)减1
int index = --count;
// 如果为0则表示达到剩余的任务数没有了,达到CyclicBarrier的条件总数了,需要唤醒其他线程
if (index == 0) {
boolean ranAction = false;
try {
// 唤醒之前的Runnable
final Runnable command = barrierCommand;
// 如果不为空的话执行其run方法
if (command != null)
command.run();
ranAction = true;
// 开启下个周期,这个方法是CyclicBarrier可以复用的原因,具体实现在下行注释
nextGeneration();
/* private void nextGeneration() {
* // 首先叫醒当前周期的其他线程,告诉其周期结束了,可以执行接下来的操作了
* trip.signalAll();
* // 然后开启下个周期,剩余任务数重置
* count = parties;
* // 下个周期
* generation = new Generation();
* }
*/
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
// 如果还不能结束本周期,就一直等待直到结束或者周期失败
for (;;) {
try {
// await的过程中是释放锁的
// 不限时的话就一直等待直到被唤醒或者打断
if (!timed)
trip.await();
else if (nanos > 0L)
// 否则的话等待一段时间后醒来
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
if (g == generation && ! g.broken) {
breakBarrier();
throw ie;
} else {
// We're about to finish waiting even if we had not
// been interrupted, so this interrupt is deemed to
// "belong" to subsequent execution.
Thread.currentThread().interrupt();
}
}
if (g.broken)
throw new BrokenBarrierException();
if (g != generation)
return index;
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
// 释放锁
lock.unlock();
}
}
ここでの基本的な理解のCyclicBarrier
実現内部、他の引数のようにawait
、それは同じロジックですが、唯一のより多くのアクセスが制限されています。
あなたが知っていれば実際には、ReentrantLock
、あなたはことを知ってCyclicBarrier
、全体が上にあることを利用。ReentrantLock
condition
3.まとめ
全体のCyclicBarrier
実装は比較的単純である、と言ったアップグレード版が過大ではありません。以下のための2つのキーポイント、タスク数とウェイクアップ動作を決定する前に、コンストラクタ、 ;別のポイント方法は、通常の状況下での各時間は、タスクの数(減少させるであろうコンストラクタによって決定された合計数)、タスクの数を時間がゼロサイクルの終わりを示すになり、目を覚ます必要がある他のスレッドを、しかし途中の上の出会いの失敗、その後、現在のサイクルが失敗し、一緒にエラーを投げ、他のスレッドを覚まします。ReentrantLock
condition
await
await
condition
障害はあなたが弱いことはありません、故障の恐れがあることでしょう。