16

この質問は2年前に尋ねられましたが、言及されているリソースがあまり役に立たない(IMHO)か、リンクが無効になっています。

理解するには、いくつかの優れたチュートリアルが必要Phaserです。javadocを読みましたが、目が眩むほどです。javadocを本当に理解するには、これらのクラスがどのように使用されるかを知っている必要があるからです。

誰か提案がありますか?

4

3 に答える 3

47

Phaserについて、私はいくつかの質問に答えました。それらを見ることは、それらのアプリケーションを理解するのに役立つかもしれません。それらは下部にリンクされています。しかし、Phaserが何をするのか、そしてなぜそれが何を解決するのかを知ることが重要である理由を理解するために。

CountdownLatchとCyclicBarrierの属性は次のとおりです

ノート:

  • パーティの数は、異なるスレッドの数を表す別の方法です
  • 再利用不可とは、再利用する前にバリアの新しいインスタンスを作成する必要があることを意味します
  • スレッドが到着して他のスレッドを待たずに作業を続行できる場合、またはすべてのスレッドが完了するのを待つことができる場合、バリアは前進可能です。

CountdownLatch

  • パーティーの固定数
  • 再利用できません
  • Advanceable(advanceableを見てlatch.countDown(); 待つ latch.await(); 必要があります)

CyclicBarrier

  • パーティーの固定数
  • 再利用可能
  • 前進できない

したがって、CountdownLatchは再利用できません。毎回新しいインスタンスを作成する必要がありますが、使用可能です。CylicBarrierは再利用できますが、すべてのスレッドは各パーティがバリアに到着するのを待つ必要があります。

Phaser

  • 動的なパーティ数
  • 再利用可能
  • 前進可能

スレッドがPhaserに認識されることを望む場合phaser.register()、スレッドが呼び出すバリアに到達したときに呼び出されます。phaser.arrive() ここで、スレッドを進めることができます。スレッドが登録されたすべてのタスクが完了するのを待ちたい場合phaser.arriveAndAwaitAdvance()

スレッドが完了していない可能性のある他の操作の完了を待機できるフェーズの概念もあります。すべてのスレッドがフェイザーのバリアに到達すると、新しいフェーズが作成されます(1ずつ増加)。

あなたは私の他の答えを見ることができます、多分それは役立つでしょう:

Java ExecutorService:再帰的に作成されたすべてのタスクのawaitTermination

柔軟なCountDownLatch?

于 2011-07-26T13:59:04.797 に答える
8

Phaser少なくとも、JavaDocはかなり明確な説明を提供していると思います。これは、スレッドのバッチを同期するために使用するクラスです。つまり、バッチ内の各スレッドをに登録し、バッチ内のすべてのスレッドがに通知するまで、を使用してブロックさせることができますPhaser。ブロックされたスレッドは実行を開始します。この待機/通知サイクルは、必要に応じて何度も繰り返すことができます。 PhaserPhaser

彼らのサンプルコードは合理的な例を示しています(私は彼らの2文字のインデントスタイルが非常に嫌いですが):

void runTasks(List<Runnable> tasks) {
   final Phaser phaser = new Phaser(1); // "1" to register self
   // create and start threads
   for (final Runnable task : tasks) {
     phaser.register();
     new Thread() {
       public void run() {
         phaser.arriveAndAwaitAdvance(); // await all creation
         task.run();
       }
     }.start();
   }

   // allow threads to start and deregister self
   phaser.arriveAndDeregister();
 } 

Phaserこれにより、登録カウントが、のが設定され、tasks.size() + 1タスクごとにnewが作成され、次の前進(つまり、到着が記録された時刻)Threadまでブロックされ、関連するタスクが実行されます。作成されたものも即座に開始されるため、到着が記録され てループから抜け出します。Phasertasks.size() + 1ThreadPhasertasks.size()

への最後の呼び出しphaser.arriveAndDeregister()は、最終的な到着を記録し、登録カウントをデクリメントして、に等しくなるようにしtasks.size()ます。これにより、Phaserが進み、事実上、すべてのタスクが同時に実行を開始できるようになります。これは、次のようなことを行うことで繰り返すことができます。

void runTasks(List<Runnable> tasks) {
   final Phaser phaser = new Phaser(1); // "1" to register self
   // create and start threads
   for (final Runnable task : tasks) {
     phaser.register();
     new Thread() {
       public void run() {
         while (true) {
           phaser.arriveAndAwaitAdvance(); // await all creation
           task.run();
         }
       }
     }.start();
   }

   // allow threads to start and deregister self
   phaser.arriveAndDeregister();
 }

...これは、タスクを繰り返し実行するループが追加されていることを除いて、以前と同じです。各反復呼び出しはタスクスレッドの実行を同期するため、他のすべてのタスクが最初の反復を完了し、2番目の反復を開始する準備ができていること phaser.arriveAndAwaitAdvance()を通知するまで、task-0は2番目の反復を開始しません。Phaser

これは、実行しているタスクの実行にかかる時間が大幅に異なる場合や、高速のスレッドが低速のスレッドと同期しなくなることのないようにする場合に役立ちます。

考えられる実際のアプリケーションについては、別々のグラフィックススレッドと物理スレッドを実行するゲームを検討してください。グラフィックススレッドがフレーム6でスタックしている場合、フレーム100の物理スレッドコンピューティングデータは必要ありません。グラフィックススレッドPhaserと物理スレッドが常に同じフレームで同時に動作するようにするための1つの可能なアプローチは、を使用することです。時間(また、一方のスレッドがもう一方のスレッドよりも大幅に遅い場合、高速のスレッドがCPUリソースを適切に生成するため、低速のスレッドがより速く追いつくことができます)。

于 2011-07-26T14:09:50.963 に答える
2

Phaserは、CyclicBarrierとCountDownLatchの機能が多少似ていますが、両方よりも柔軟性があります。

CyclicBarrierでは、以前はコンストラクターにパーティを登録していましたが、Phaserを使用すると、いつでもパーティを登録および登録解除できる柔軟性が得られます。パーティーの登録には、次のいずれかを使用できます-

  • コンストラクター
  • 登録、または
  • BulkRegister

登録解除パーティには、-を使用する場合があります

  • 到着して登録解除、または


register- このフェイザーに新しい未到着のパーティを追加/登録します。戻ります

  • この登録が適用された到着フェーズ番号。
  • フェーザーが終了した場合、値は負であり、登録は効果がありません。

onAdvance()メソッドの呼び出しが、戻る前よりも進行中の場合、このメソッドはその完了を待つ可能性があります。このフェイザーに親があり、このフェイザーに登録されているパーティがない場合、この子フェイザーもその親に登録されます。
Phaserの使用法を示すプログラム>

import java.util.concurrent.Phaser;

public class PhaserTest {
public static void main(String[] args) {

       /*Creates a new phaser with 1 registered unArrived parties 
        * and initial phase number is 0
        */
       Phaser phaser=new Phaser(1);
       System.out.println("new phaser with 1 registered unArrived parties"
                    + " created and initial phase  number is 0 ");

       //Create 3 threads
       Thread thread1=new Thread(new MyRunnable(phaser,"first"),"Thread-1");
       Thread thread2=new Thread(new MyRunnable(phaser,"second"),"Thread-2");
       Thread thread3=new Thread(new MyRunnable(phaser,"third"),"Thread-3");

       System.out.println("\n--------Phaser has started---------------");
       //Start 3 threads
       thread1.start();
       thread2.start();
       thread3.start();

       //get current phase
       int currentPhase=phaser.getPhase();
       /*arriveAndAwaitAdvance() will cause thread to wait until current phase
        * has been completed i.e. until all registered threads
        * call arriveAndAwaitAdvance()
        */
       phaser.arriveAndAwaitAdvance();
       System.out.println("------Phase-"+currentPhase+" has been COMPLETED----------");

       //------NEXT PHASE BEGINS------

       currentPhase=phaser.getPhase();
       phaser.arriveAndAwaitAdvance();
       System.out.println("------Phase-"+currentPhase+" has been COMPLETED----------");

       /* current thread Arrives and deRegisters from phaser.
        * DeRegistration reduces the number of parties that may
        * be required to advance in future phases.
        */
       phaser.arriveAndDeregister();

       //check whether phaser has been terminated or not.
       if(phaser.isTerminated())
              System.out.println("\nPhaser has been terminated");

} 
}





class MyRunnable implements Runnable{

Phaser phaser;

MyRunnable(Phaser phaser,String name){
       this.phaser=phaser;
       this.phaser.register(); //Registers/Add a new unArrived party to this phaser.
       System.out.println(name +" - New unarrived party has "
                    + "been registered with phaser");
}

@Override
public void run() {
       System.out.println(Thread.currentThread().getName() +
                    " - party has arrived and is working in "
                    + "Phase-"+phaser.getPhase());
       phaser.arriveAndAwaitAdvance();

       //Sleep has been used for formatting output
       try {
              Thread.sleep(1000);
       } catch (InterruptedException e) {
              e.printStackTrace();
       }

       //------NEXT PHASE BEGINS------

       System.out.println(Thread.currentThread().getName() +
                    " - party has arrived and is working in "
                    + "Phase-"+phaser.getPhase());
       phaser.arriveAndAwaitAdvance();  

       phaser.arriveAndDeregister();
}

}


BulkRegister- このフェイザーに未到着の新しいパーティのパーティ数を追加します。戻ります

  • この登録が適用された到着フェーズ番号。
  • フェーザーが終了した場合、値は負であり、登録は効果がありません。

onAdvance()メソッドの呼び出しが、戻る前よりも進行中の場合、このメソッドはその完了を待つ可能性があります。

ArrivateAndDeregister- 現在のスレッド(パーティ)フェイザーに到着し、フェイザーから登録解除します。登録解除により、次のフェーズに進むために将来必要になる可能性のあるパーティの数が減ります。

このフェイザーに親があり、このフェイザーに登録されているパーティがない場合、この子フェイザーもその親に登録されます。 親子フェイザーをデモンストレーションするプログラム

import java.util.concurrent.Phaser;

public class PhaserParentChildTest {
public static void main(String[] args) {


    /*
  * Creates a new phaser with no registered unArrived parties.
  */
    Phaser parentPhaser = new Phaser();

    /*
  * Creates a new phaser with the given parent &
  * no registered unArrived parties.
  */
    Phaser childPhaser = new Phaser(parentPhaser,0);

    childPhaser.register();

    System.out.println("parentPhaser isTerminated : "+parentPhaser.isTerminated());
    System.out.println("childPhaser isTerminated : "+childPhaser.isTerminated());

    childPhaser.arriveAndDeregister();
    System.out.println("\n--childPhaser has called arriveAndDeregister()-- \n");

    System.out.println("parentPhaser isTerminated : "+parentPhaser.isTerminated());
    System.out.println("childPhaser isTerminated : "+childPhaser.isTerminated());

}
}

Phaserの詳細については、http: //www.javamadesoeasy.com/2015/03/phaser-in-java_21.htmlをご覧ください。

于 2015-05-08T20:10:19.827 に答える