Java:jsr166y Phaser的教程/解释

时间:2011-07-26 13:41:47

标签: java concurrency phaser

两年前曾问过{p> This question,但它提到的资源要么不是很有用(恕我直言),要么链接不再有效。

必须有一些好的教程才能理解Phaser。我已经阅读了javadoc,但是我的眼睛茫然,因为为了真正理解javadoc,你必须知道应该如何使用这些类。

有人有任何建议吗?

3 个答案:

答案 0 :(得分:45)

对于Phaser,我已经回答了几个问题。看到它们可能有助于理解它们的应用。它们在底部相连。但要了解Phaser的作用以及为什么它有用,重要的是要知道它解决了什么。

以下是CountdownLatch和CyclicBarrier的属性

注意:

  • 当事人的数量是说出不同线程的另一种方式
  • 不可重用意味着您必须创建新的实例 重用之前的障碍
  • 如果线程可以到达并继续工作,则可以推进屏障 无需等待其他人或可以等待所有线程完成

<强> CountdownLatch

  • 固定数量的派对
  • 不可恢复
  • 可推进(查看latch.countDown(); 可推进 latch.await(); 必须等待

<强> 的CyclicBarrier

  • 固定数量的派对
  • 可重复使用
  • 不可推进

因此CountdownLatch不可重复使用,您必须每次都创建一个新实例,但是可以使用。可以重复使用CylicBarrier,但所有线程必须等待每一方到达屏障。

<强> 移相

  • 动态参加方数
  • 可重复使用
  • 可推进

当一个线程想要被Phaser知道时,当线程到达屏障时,他们调用phaser.register(),他们调用phaser.arrive() ,这里是可推进的。如果线程想要等待所有已注册的任务完成phaser.arriveAndAwaitAdvance()

还有一个阶段的概念,其中线程可以在完成其他可能尚未完成的操作时等待。一旦所有线程到达移相器的障碍,就会创建一个新阶段(增量为1)。

你可以看看我的其他答案,也许会有所帮助:

Java ExecutorService: awaitTermination of all recursively created tasks

Flexible CountDownLatch?

答案 1 :(得分:5)

至少对于Phaser,我认为JavaDoc提供了一个相当清晰的解释。这是一个用于同步一批线程的类,在这种意义上,您可以使用Phaser注册批处理中的每个线程,然后使用Phaser阻止它们直到每个线程在批处理中已通知Phaser,此时任何被阻塞的线程将开始执行。该等待/通知周期可以根据需要/要求一遍又一遍地重复。

他们的示例代码给出了一个合理的例子(虽然我非常不喜欢他们的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,并且每个任务都会创建一个新的Thread,它会阻止Phaser的下一个进度(即记录tasks.size() + 1到达的时间,然后运行其相关任务。创建的每个Thread也会立即启动,因此Phaser会在循环中出现tasks.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();
 }

...这与之前相同,只是添加了一个循环,导致任务重复运行。因为每次迭代都会调用phaser.arriveAndAwaitAdvance(),所以任务线程的执行将被同步,以便task-0不会开始第二次迭代,直到每个其他任务完成第一次迭代并通知已准备好的Phaser开始第二次迭代。

如果您运行的任务在执行时花费的时间差异很大,并且您希望确保更快的线程不会与较慢的线程不同步,那么这可能很有用。

对于可能的实际应用程序,请考虑运行单独的图形和物理线程的游戏。如果图形线程卡在第6帧上,您不希望拥有第100帧的物理线程计算数据,并且使用Phaser是一种可能的方法来确保图形和物理线程始终在相同的帧同时(并且如果一个线程明显慢于另一个线程,则更快的线程优雅地产生CPU资源,以便更慢的线程可以更快地赶上)。

答案 2 :(得分:2)

Phaser在功能上与CyclicBarrier和CountDownLatch有些相似,但它提供了比两者更多的灵活性。

在CyclicBarrier中,我们曾经在构造函数中注册方,但Phaser在任何时候都为我们提供了注册和取消注册方的灵活性。  对于注册方,我们可以使用以下任何一种方式 -

     
  • 构造
  •  
  • 注册,或
  •  
  • bulkRegister

对于 deRegistering 方,我们可以使用 -

     
  • 到达和注销,或


注册 - 添加/注册此移相器的新未命名方。它返回

  • 此注册所适用的到达阶段编号。
  • 如果移相器已终止,则值为负数且注册无效。

如果onAdvance()方法的调用正在进行,则返回此方法可能等待其完成。 如果此移相器具有父级,并且此移相器没有注册方,则此子移相器也会向其父级注册。
演示使用Phaser&gt;

的程序
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()方法的调用正在进行,则返回此方法可能等待其完成。

arriveAndDeregister - 的 当前线程(Party)从移相器到达和取消注册。 DeRegistration减少了将来可能需要进入下一阶段的各方数量。

如果此移相器具有父级,并且此移相器没有注册方,则此子移相器也会向其父级注册。 计划展示父母和子女Phaser

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());

}
}

http://www.javamadesoeasy.com/2015/03/phaser-in-java_21.html

上阅读有关Phaser的更多信息