新しいPhaser Synchronizer

Phaserは、 バリア同期パターンの強力で柔軟な実装です。 java.util.concurrentパッケージの一部としてJDK 7に含まれています。



彼らが言うように、ドキュメントは100グラムなしでは理解できないので、ここで動作原理、非自明な点を説明し、いくつかの使用例を示します。







Phaserは、JDK 1.5の前身であるCyclicBarrierの機能を非常に自然に拡張します(詳細については、 こちらを参照しください )。 ちなみに、この拡張機能は非常に一般的であるため、標準ライブラリCountDownLatchの3番目のバリアの規約と一致しています。





メーカーの状態には以下が含まれます
  1. ステージ番号(フェーズ、同期サイクル)| intフェーズ
  2. 参加者数| intパーティー
  3. 準備を宣言した/宣言しなかった参加者の数| intが到着、未到着
  4. 完了状態| ブール終了
常に真実:

終了= false→フェーズ= [実際のステージ番号、ゼロからカウント]%(2 31-1)

パーティー=到着+到着なし

0≤未到着、到着≤パーティー



すべての状態要素にはゲッターが装備されています。





0.ステージマネージャーが終了状態(終了= true)の場合、ステージマネージャーは変更できません。 制御メソッドの呼び出しはすぐに戻ります。 フェーズは負の値、パーティー、到着、未到着- 完了時の値を持ちます。





1.主な制御方法:

登録() 会員登録
到着() ステープラーに準備ができたことを知らせ、バリアが開くのを待たない
ArrivedAndAwaitAdvance() 障壁に古典的な到着。 CyclicBarrier.await()の正確な対応物
ArrivedAndDeregister() 参加を取り消す
もちろん、JDKの他のシンクロナイザーと同様に、制御メソッドの呼び出しスレッドは監視されないため、「メンバースレッド」や「自己登録」などの用語は任意です。 つまり、装填されたピストルはすでに手元にあり、それを足に向けて引き金を引くように残っています:-)しかし、この危険な状況を修正するラッパーを書くことは難しくありません。





2.バリアは、 未到達のゼロへの減少の直後に開きます。 1つまり、最後の参加者が削除されたときを含むが、「空の」ステージマネージャー(新しいPhaser()または新しいPhaser(0))を作成すると、ゲートは閉じられます。



何らかの方法で、「到着」で始まるメソッドの1つを呼び出すことによってのみ、障壁を克服することができます。 これを行うスレッドのコンテキストでは、onAdvance(フェーズ、パーティ)プロテクトメソッドが実行されます-trueが返された場合、ステージデザイナーは作業を終了します(終了←true)。 このメカニズムにより、クラス内からライフサイクルを管理できます。 デフォルトの実装では、すべての参加者がバリアを離れた場合(parties = 0)にフェイザーが停止します。



障壁を開くことは、新しい段階への移行です。フェーズ←フェーズ+ 1。



おおよその仕組み:

final Phaser ph = new Phaser(4); ph.arrive(); ph.register(); new Thread() {public void run(){ ph.arriveAndAwaitAdvance(); }}.start(); ph.arrive(); ph.arrive(); ph.arriveAndDeregister(); // phase number = 1 // Thread-0 released
      
      





画像

メソッドは、その原因となった状態の上に記述されます





3. ArrivedAndDeregister-メソッドの名前が示唆するように、ストリームが最初に準備完了を宣言してから削除されると考えるのは有害です。 本質は、「arriveToDeregister」、「パーティーカードをテーブルに置くようになった」を反映します:-)このアプローチは、理解の曖昧さを解消しますか(上の図を参照):新しいステージに進む前に、引数party = 4または5でonAdvance()と呼ばれますか?





4. onAdvanceメソッドの完了ロジックを変更する例。 「殺せない」ステージングマシンは次のように構築されます。

 new Phaser() { protected boolean onAdvance(int phase, int parties) { return false; } };
      
      





メソッドでも、いわゆるを実行できます。 バリアアクション-参加者の作業の結果の構成など。



具体的には、次の段階に移行するとどうなりますか:

画像





使用する



Phaser CyclicBarrierから取得するには、arrivateAndAwaitAdvance()メソッドを使用します。



CountDownLatchのエミュレートはもう少し複雑です。

 // aka countDown() phaser.arriveAndDeregister(); //  arrive()    // aka await() if (!phaser.isTerminated()) //  phaser.awaitAdvance( phaser.getPhase() );
      
      





awaitAdvance(getPhase())構造は安全です(アトミックではありませんが)。awaitAdvance(int phaseNumber)メソッドは、指定されたステップが既に通過している場合、すぐに戻るためです。





一般に、ステージカウンターを使用すると、寿命が大幅に簡素化され、クラスアプリケーションの数が増えます。



連続したN個の同一アクションの並列化:

 import java.util.concurrent.Phaser; public class FBIEasterEgg { static int lines = 10; static String alphabet = "abcdefghijklmnopqrstuvwxyz"; static StringBuffer randomAlphabet = new StringBuffer(); public static void main(String[] args) { final Phaser phaser = new Phaser() { protected boolean onAdvance(int phase, int parties) { // John Nash mode /* print "deviations" for (int i = 0; i < alphabet.length(); i++) { System.out.printf("%d ", randomAlphabet.indexOf( alphabet.charAt(i) + "") - i); } System.out.println(); */ System.out.println(randomAlphabet); // randomAlphabet = new StringBuffer(); return phase >= lines; //loop count managing here } }; // everyone have to wait for the main thread phaser.register(); for (int i = 0; i < alphabet.length(); i++) { final char next = alphabet.charAt(i); phaser.register(); // ticket for the following thread new Thread() { public void run() { do { randomAlphabet.append(next); phaser.arriveAndAwaitAdvance(); // checkout } while ( !phaser.isTerminated() ); } }.start(); } System.out.println("Write this by hand and carry in the pocket:"); // some additional preparations may be done here // release phaser.arriveAndDeregister(); } }
      
      







指定された数のイベントが発生するまで待機します。

 class EventCounter { private Phaser count = new Phaser(1); public void eventOccured() { count.arrive(); } public void waitFor(int events) { count.register(); for (int i = 0; i < events; i++) { count.arriveAndAwaitAdvance(); } count.arriveAndDeregister(); } }
      
      









実装



Doug Leeは機知に富んだアイデアを使用しました:ステージワーカーの状態全体を1つの長いものに格納し、アトミックな比較と設定操作によって変更します。

JDK 7のPhaser

このため、実装は通常のロックをほとんど使用しません。





参照資料



フェイザーjavadoc

JDK 7ダウンロードページ

Concurrency JSR-166 Interest Site -JDK 6用の新しいjava.util.concurrentをダウンロードするためのリンクがあります。





P. S.名前「The Maker」は、もちろん、誰かがより良く知っている/考えているなら、不器用です-隠さないでください。

1-フェイザーが階層のルートである場合。 このレビューでは、スケーリングメカニズムは考慮されていません(とにかく嘘をついていたでしょう)。javadocを参照してください。



All Articles