アウトライン
等のタスクを実行した後、いくつかのスレッドが、(任意のコールバック関数、)別のスレッドを実行し、その後、次のラウンドに進み、そして:CyclicBarrierをと契約クラスでツールであり、それは典型的なアプリケーションのシナリオです。
人気の例を作成し、CyclicBarrierを実行フローに例えることができます:少数の人々(類推スレッド)遊び場の周りにラップ、誰もが最後に到達(最後は「バリア(障壁)」として理解、順序が到着することができる持っています対応するスレッドは実行速度タスク)、アクション(コールバック関数)を実行し、等々(次の次のラップを実行する)のサイクルを継続し、。
前者は次の分析のためのコード「ループ」動作であるが、「使い捨て(ワンショット)」操作として理解することができるようなたCountDownLatchと比較。
コード分析
次のようにCyclicBarrierを主な方法は以下のとおりです。
一般的に2で使用されているが、方法を待って、待ち状態に現在のスレッドをさせることです。
メンバー変数およびネストされたカテゴリ:
// 内部嵌套类 プライベート 静的 クラス世代{ ブール壊れ= 偽; } / ** バリアエントリを守るためのロック* / プライベート 最終 ReentrantLockのロック= 新しいReentrantLockの()。 / ** 条件にトリップするまでに待つ* / プライベート 最終条件の旅= lock.newCondition(); / ** 当事者の数* / 民間 最終 int型の当事者。 / * つまずいたときに実行するコマンド* / 民間 最終RunnableをbarrierCommand; / ** 現世代* / プライベート・ジェネレーション世代= 新世代(); / ** *当事者の数はまだ待っています。0の当事者からカウントダウンし 、各世代の*。これは、それぞれの新しい上の当事者にリセットされます *世代や壊れたとき。 * / プライベート int型の数。
世代は、内側のネストされた代数的クラスを表し、各障壁(バリア)が次世代を入力した後、前に損傷の同世代に属します。
コンストラクタ
// 无回调函数 パブリック(CyclicBarrierをINT 当事者){ この(パーティー、NULL ); } // 有回调函数 公共 CyclicBarrierを(int型のパーティ、RunnableをbarrierAction){ 場合(当事者<= 0)スロー 新しい、IllegalArgumentExceptionを()。 これは = .parties パーティーを。 この .count = 関係者; この .barrierCommand = barrierAction。 }
2つのコンストラクタ、コールバック関数(barrierAction)を通過することができる後者を有するCyclicBarrierを、当事者は、呼び出し側スレッドを待つ表します。
メソッドを待ちます
// 阻塞式等待 公共 int型は()待つスローInterruptedExceptionあるが、BrokenBarrierExceptionが{ しようと{ 戻り dowait(偽、0L ); } キャッチ(TimeoutExceptionつま先){ スロー 新しいエラー(つま先)。// 起こることができない } } // 有超时的等待 公共 INT待つ(長いタイムアウトは、TimeUnitで単位)が スローInterruptedExceptionある、 BrokenBarrierException、 TimeoutExceptionを{ 戻り dowait(真、unit.toNanos(タイムアウト)); }
次のように我々は、呼び出される2つの方法が達成するdowait方法を待つ(これものコアの方法CyclicBarrierをである)を参照することができます。
プライベート int型 dowait(ブールタイミング、長いnanos値)は スローInterruptedExceptionある、BrokenBarrierException、 TimeoutException { 最終 ReentrantLockのロック= 本の.lockと、 lock.lock(); 試す{ // 获取当前代 最終生成G = 世代。 // 若屏障破坏、则抛出异常 場合(g.broken) スロー 新しいBrokenBarrierException(); もし(Thread.interrupted()){ breakBarrier()。 スロー 新しい例外:InterruptedExceptionを(); } //カウント数を保存します1つの。 int型のインデックス= - カウント; IF(インデックス== 0){ // TRIPPED // 0に減少したときの動作はカウントがトリガされ 、ブール ranAction = falseに、 試してみる{ // 渡されたコールバック 決勝のRunnableコマンド= barrierCommand ; IF(コマンド=!ヌル) // コールバック関数、コールバック関数を渡した場合 // PS:それは見ることができ、最後のスレッド実行の最後で実行されるコールバック関数 command.run(); ranAction =真の; // 次の世代(次の動作)に 次世代(); 戻り 0 ; } 最後に{ IF(!ranAction) breakBarrier(); } } // ループまでTRIPPED、壊れ中断、または時限OUT のための(。 ;){ 試み{ // COUNTは0、待ち状態に現在のスレッドではありません IF(!時限) trip.await(); 他の IF(またはnanos> 0L ) nanos値 =trip.awaitNanos(nanos値)。 } キャッチ(InterruptedExceptionあるIE){ 場合(G ==生成&&!g.broken){ breakBarrier()。 スローすなわち、 } 他{ // 私たちはしていなかった場合でも、待ってフィニッシュしようとしている // 中断され、ので、この割り込みがあると思われる // 以降の実行に「属しています」。 Thread.currentThread()の割り込み(); } } もし(g.broken) スロー 新しい(BrokenBarrierExceptionを)。 もし(!G = 世代) リターン指数。 もし(時限&& nanos値<= 0L ){ breakBarrier()。 スロー 新しいTimeoutExceptionを(); } } } 最後に{ lock.unlock()。 } }
次世代和breakBarrier:
// 进入下一轮 プライベート ボイド次世代(){ // 最後の世代の信号完了 trip.signalAll()。 //は、次世代の設定 数= 当事者に; 世代 = 新世代()。 } // 破坏屏障 プライベート ボイドbreakBarrier(){ generation.broken = 真。 カウント = 当事者; trip.signalAll(); }
実行処理:同じ当事者と初期カウント値(コンストラクタ当事者によって渡されるパラメータ)、カウントがゼロ(0待機していない場合)、実行されるまで、カウント値が1だけデクリメントされる待つ起動毎に一つのスレッド後入ってくるコールバック関数barrierCommand(空でない場合)、その後、すべてのスレッドを覚ますと、アクションの次のラウンドを開始するには、関係者のカウントをリセットします。
シナリオ例の場合
理解CyclicBarrierを使用状況を容易にするために、以下の実施例は、単純な(参照用のみ):
パブリック クラスCyclicBarrierTest { プライベート 静的 最終 INT。COUNT = 3 ; 公共 静的 ボイドメイン(文字列[]引数)がスローInterruptedExceptionある{ // 初期化とコールバックがCyclicBarrierをオブジェクト CyclicBarrierをCyclicBarrierを= 新しい新しい - > CyclicBarrierを(COUNT(){ // アナログコールバックを操作機能(アナログライト) のSystem.out.println(にThread.currentThread()のgetName()+ "を書き込む..開始。" ); 試み{ TimeUnit.SECONDS.sleep( 1 ); } キャッチ(InterruptedExceptionあるE){ e.printStackTrace(); } のSystem.out.println( "---------" ); }); 一方、(真の){ // タスクを実行するために複数のスレッドを作成 するための(int型 I = 0、I <COUNT; I ++は){ 新しい新しいスレッド(() - > { // アナログ読み のSystem.out.println(にThread.currentThread()のgetName()+ "読んでいる..." ); 試み{ TimeUnit.SECONDS.sleep ( 3 ); // 待つ ; CyclicBarrier.await() } キャッチ(InterruptedExceptionあります| {BrokenBarrierException E) ; e.printStackTrace() } })を起動();. } // 10秒間スリープし、次のラウンドに TimeUnit.SECONDS.sleep(10 ); } } } / * 実行結果(参考のためにのみ): スレッド0が..です読み取る -スレッドである1お読み。 スレッド-2 ..です読み取る スレッド開始-書き込みを1 .. --------- スレッドが-IS 3お読み。 スレッド読む... IS -4 スレッド-IS。5お読み スレッドのスタートを。5 -書き込み.. --------- * /
PS:ここで、書き込み動作が行われた後、すべての読み取り、読み取り操作を実行するために複数のスレッドをシミュレートし、リードした後、書き込み......簡単な調整システムとして理解することができます。
ここに参照のためのコードは、クラスの使用の理解を容易にします。実際には、すべてのスレッドの作成は(何の分析は、ここでは使用されませんので、スレッドプールを使用することができます)無理があります。
概要
CyclicBarrierをもたCountDownLatchに幾分類似しているカウンタの逆数として理解することができます。前者が後者を「リサイクル」、「単発」であります
渇望する愚か者であれ。
PS:この記事では、最初の公開マイクロチャネル番号] [WriteOnReadに登場しました。