CyclicBarrier
呼ばれ、ループフェンスは、フェンスで実行されていると、すべてのスレッドがフェンスに到達するまでの機能をブロックするスレッドのセットを許可するように達成した後、一緒に実行することができます。「ループ」の手段CyclicBarrier
に比べ、繰り返し使用することができ、CountDownLatch
一度だけ使用すること、CyclicBarrier
あなたは多くのリソースを節約することができ、また、フェンスの条件が満たされた場合、このタスクを実行するには、コンストラクタでタスクを渡すことができます。CyclicBarrier
使用されているReentrantLock
実行している間、主な方法はとても同時パフォーマンスは非常に高いものではない、ロックします。
1.関連分野
//重入锁,CyclicBarrier内部通过重入锁实现线程安全
private final ReentrantLock lock = new ReentrantLock();
//线程阻塞时的等待条件
private final Condition trip = lock.newCondition();
//需要等待的线程数
private final int parties;
//栅栏打开之后首先执行的任务
private final Runnable barrierCommand;
//记录当前的分代标记
private Generation generation = new Generation();
//当前还需要等待多少个线程运行到栅栏位置
private int count;
ことに注意してくださいgeneration
フィールドが世代で現在のフェンスをマークするために使用。特定の条件が満たされている(例えば、呼び出されたときにreset
方法など、または柵が開いている)、状態は次のフェンスに切り替わり、実際にnew
新規Generation
であるオブジェクト、CyclicBarrier
内部クラス次のように、コードは、非常に簡単です。
private static class Generation {
boolean broken = false; //标记栅栏是否被破坏
}
実際の使用では、我々が使用するgeneration
使用して、同じ世代で現在かどうかを決定するフィールドをbroker
フィールドバリアが破壊されたか否かを判定する。
2.コンストラクタ
CyclicBarrier
2つのオーバーロードされたコンストラクタは、単純に次のように初期化されている上記の分野に関連するコンストラクタがあります。
public CyclicBarrier(int parties) {
this(parties, null);
}
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
前記コア法
await
await
同じ開発の最も一般的に使用される方法であるCountDownLatch
ように、CyclicBarrier
また2つの提供await
方法、パラメータなしで1をタイムアウトパラメータでは、単に少し内側に呼び出すdowait
方法:
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
public int await(long timeout, TimeUnit unit)
throws InterruptedException,
BrokenBarrierException,
TimeoutException {
return dowait(true, unit.toNanos(timeout));
}
そして、見て重要な取るdowait
方法を:
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
//加重入锁
lock.lock();
try {
//首先获取年龄代信息
final Generation g = generation;
//如果栅栏状态被破坏,抛出异常,例如先启动的线程调用了breakBarrier方法,后启动的线程就能够看到g.broker=true
if (g.broken)
throw new BrokenBarrierException();
//检测线程的中断状态,如果线程设置了中断状态,则通过breakBarrier设置栅栏为已破坏状态,并唤醒其他线程
//如果这里能够检测到中断状态,那只可能是在await方法外部设置的
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
//每调用一次await,就将需要等待的线程数减1
int index = --count;
//index=0表示这是最后一个到达的线程,由该线程执行下面的逻辑
if (index == 0) { // tripped
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
//如果在构造器中传入了第二个任务参数,就在放开栅栏前先执行这个任务
if (command != null)
command.run();
ranAction = true;
//正常结束,需要唤醒阻塞的线程,并换代
nextGeneration();
return 0;
} finally {
//try代码块如果正常执行,ranAction就一定等于true,而try代码块唯一可能发生异常的地方就是command.run(),
//因此这里为了保证在任务执行失败时,将栅栏标记为已破坏,唤醒阻塞线程
if (!ranAction)
breakBarrier();
}
}
// loop until tripped, broken, interrupted, or timed out
for (;;) {
try {
//没有设置超时标记,就加入等待队列
if (!timed)
trip.await();
//设置了超时标记,但目前还没有超时,则继续等待
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
//如果线程等待的过程中被中断,会执行到这里
//g == generation表示当前还在同一个年龄分代中,!g.broker表示当前栅栏状态没有被破坏
if (g == generation && ! g.broken) {
breakBarrier();
throw ie;
} else {
//上面的条件不满足,说明:1)g!=generation,说明线程执行到这里时已经换代了
//2)没有换代,但是栅栏被破坏了
//无论哪种情况,都只是简单地设置一下当前线程的中断状态
Thread.currentThread().interrupt();
}
}
//栅栏被破坏,抛出异常
//注意,在breakBarrier方法中会唤醒所有等待条件的线程,这些线程会执行到这里,判断栅栏已经被破坏,都会抛出异常
if (g.broken)
throw new BrokenBarrierException();
//距离上一次设置g变量的值已经过去很长时间了,在执行过程中generation可能已经发生改变,
//当前线程还是前几代的,不需要再循环阻塞了,直接返回上一代剩余需要等待的线程数
//注意:代码中breakBarrier方法和nextGeneration方法都会唤醒阻塞的线程,但是breakBarrier在上一个判断就被拦截了,
//因此走到这里的有三种情况:
//a)最后一个线程正常执行,栅栏打开导致其他线程被唤醒;不属于当前代的线程直接返回,
//属于当前代的则可能因为没到栅栏开放条件要继续循环阻塞
//b)栅栏被重置(调用了reset方法),此时g!=negeration,全都直接返回
//c)线程等待超时了,不属于当前代的返回就可以了,属于当前代的则要设置generation.broken = true
if (g != generation)
return index;
//如果线程等待超时,标记栅栏为破坏状态并抛出异常,如果还没超时,则自旋后又重新阻塞
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
//别忘了解锁
lock.unlock();
}
}
dowait
メソッドのロジックは次のとおりです。各呼び出しawait
スレッド数にはなりますcount
減少し1
、最後のスレッドがされるcount
まで減少0
時、偶然にも行ってbarrierCommand
指定されたタスクとgeneration
次の世代への切り替えを、当然のことながら、最も重要なことは、フェンスの前に目を覚ますことですブロックされたスレッドで。以下のためにtrip
、対応するCondition
オブジェクトが場所がない変更されますので、trip.signalAll()
待機中のプロセス内のスレッドは、他のスレッドがする場合は、すべて、条件待機中のスレッドに目を覚ますだろうgeneration
次の世代に更新され、いくつかのも属しスレッドが起こされるだろうその世代の状況に先立っ。
次はオンになりますdowait
簡単な紹介のために使用される方法のいくつか。
breakBarrier
dowait
4つのローカルメソッド呼び出しがありますがbreakBarrier
、あなたがされる名前から見ることができますgeneration.broken
設定true
以外に、復元され、count
値を、すべてのブロックされたスレッドをウェイクアップ:
private void breakBarrier() {
generation.broken = true;
count = parties;
//唤醒所有的阻塞线程
trip.signalAll();
}
見るとCyclicBarrier
、ソースコード、generation.broken
統一はbreakBarrier
アプローチに設定されているtrue
が、一度generation.broken
セットtrue
後に、この状態に例外をスローするコードを確認した後、に方法はありません再使用フェンス(手動で呼び出すことができますreset
リセット)、およびソースコードを次のような場合に呼び出すbreakBarrier
方法:
1)現在のスレッドが中断された
失敗2)コンストラクタタスクを通過を
待って中断3)条件
TIMEOUT 4)スレッドを待つ
5)を明示的に呼び出すreset
方法を
nextGeneration
private void nextGeneration() {
// 唤醒所有的阻塞线程
trip.signalAll();
// 开启下一代
count = parties;
generation = new Generation();
}
reset
reset
主な方法は、この世代の終わりであり、そして次の世代に切り替えます
public void reset() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
breakBarrier(); // break the current generation
nextGeneration(); // start a new generation
} finally {
lock.unlock();
}
}
ここで紹介し、全体をCyclicBarrier
ほとんどかけて導入されていますが、ロジックが大部分でカプセル化されているため、内部プロセスは、単純なものから遠く離れているAbstractQueuedSynchronizer
スレッドがウェイクアップされるのを待っているキューをブロックするだけでなく、どのようにしているか、このクラスの定義は、あなたがしたいそうだとすれば、待機への論理スレッドの深さを理解するだけでなく、慎重に研究する必要があるAbstractQueuedSynchronizer
仕事を。この記事では、コンテンツのこの部分は時間があれば、特別なプレゼンテーションになります背後ではありませんについて説明します。