Java并发编程-同步辅助类之Phaser

在前面已经学习过 
Java并发编程-同步辅助类之CountDownLatch 
Java并发编程-同步辅助类之CyclicBarrier 
这篇文章介绍另一个辅助类Phaser,它是Jdk 1.7才添加的新的功能,它可以实现和CountDownLatch/CyclicBarrier类似的功能,但Phaser的功能更多,更加的灵活,它支持任务在多个点都进行同步,支持动态调整注册任务的数量。当然你也可以使用CountDownLatch,但你必须创建多个CountDownLatch对象,每一阶段都需要一个CountDownLatch对象。

操作方法

  • Phaser(int parties),构造方法,与CountDownLatch一样,传入同步的线程数,也支持层次构造Phaser(Phaser parent)。

  • register(),bulkRegister(int Parties),动态添加一个或多个参与者。

  • arriveAndDeregister()方法,动态撤销线程在phaser的注册,通知phaser对象,该线程已经结束该阶段且不参与后面阶段。

  • isTerminated(),当phaser没有参与同步的线程时(或者onAdvance返回true),phaser是终止态(如果phaser进入终止态arriveAndAwaitAdvance()和awaitAdvance()都会立即返回,不在等待)isTerminated返回true。

  • arrive()方法,通知phaser该线程已经完成该阶段,但不等待其他线程。

  • arriveAndAwaitAdvance()方法,类似await()方法,记录到达线程数,阻塞等待其他线程到达同步点后再继续执行。

  • awaitAdvance(int phase) /awaitAdvanceInterruptibly(int phase) ,传入阶段数,只有当前阶段等于phase阶段时才阻塞等待。后者如果线程在休眠被中断会抛出InterruptedException异常(phaser的其他方法对中断都不会抛出异常)。

  • onAdvance(int phase, int registeredParties)方法。参数phase是阶段数,每经过一个阶段该数加1,registeredParties是当前参与的线程数。此方法有2个作用:1、当每一个阶段执行完毕,此方法会被自动调用,因此,重载此方法写入的代码会在每个阶段执行完毕时执行,相当于CyclicBarrier的barrierAction。2、当此方法返回true时,意味着Phaser被终止,因此可以巧妙的设置此方法的返回值来终止所有线程。例如:若此方法返回值为 phase>=3,其含义为当整个线程执行了4个阶段后,程序终止。

  • forceTermination()方法,强制phaser进入终止态。

使用实例

对在CountDownLatch中的例子使用Phaser代替CountDownLatch进行改进:

package MyThread;

import java.util.concurrent.Phaser;

public class Match {

    // 模拟了100米赛跑,10名选手,只等裁判一声令下。当所有人都到达终点时,比赛结束。
    public static void main(String[] args) throws InterruptedException {

        final Phaser phaser=new Phaser(1) ;  
        // 十名选手 
        for (int index = 0; index < 10; index++) {
            phaser.register();
            new Thread(new player(phaser),"player"+index).start();
        }  
        System.out.println("Game Start");  
        //注销当前线程,比赛开始
        phaser.arriveAndDeregister();
        //是否非终止态一直等待
        while(!phaser.isTerminated()){          
        }
        System.out.println("Game Over");
    }
}
class player implements Runnable{

    private  final Phaser phaser ;  

    player(Phaser phaser){
        this.phaser=phaser;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        try {  
            // 第一阶段——等待创建好所有线程再开始
            phaser.arriveAndAwaitAdvance();  

            // 第二阶段——等待所有选手准备好再开始

            Thread.sleep((long) (Math.random() * 10000)); 
            System.out.println(Thread.currentThread().getName() + " ready");   
            phaser.arriveAndAwaitAdvance();  

            // 第三阶段——等待所有选手准备好到达,到达后,该线程从phaser中注销,不在进行下面的阶段。
            Thread.sleep((long) (Math.random() * 10000)); 
            System.out.println(Thread.currentThread().getName() + " arrived");   
            phaser.arriveAndDeregister(); 
        } catch (InterruptedException e) { 
            e.printStackTrace();          
        } 
    }
}

3 Sample Usage

3.1 Sample 1

    在有些场景下,我们希望控制多个线程的启动时机:例如在并发相关的单元测试中,有时需要控制线程的启动时机,以期获得最大程度的并发,通常我们会使用CountDownLatch,以下是使用Phaser的版本。

Java代码   收藏代码
  1. import java.util.concurrent.Phaser;  
  2.   
  3. public class PhaserTest1 {  
  4.   
  5.     public static void main(String args[]) {  
  6.         //  
  7.         final int count = 5;  
  8.         final Phaser phaser = new Phaser(count);  
  9.         for(int i = 0; i < count; i++) {  
  10.             System.out.println("starting thread, id: " + i);  
  11.             final Thread thread = new Thread(new Task(i, phaser));  
  12.             thread.start();  
  13.         }  
  14.     }  
  15.       
  16.     public static class Task implements Runnable {  
  17.         //  
  18.         private final int id;  
  19.         private final Phaser phaser;  
  20.   
  21.         public Task(int id, Phaser phaser) {  
  22.             this.id = id;  
  23.             this.phaser = phaser;  
  24.         }  
  25.           
  26.         @Override  
  27.         public void run() {  
  28.             phaser.arriveAndAwaitAdvance();  
  29.             System.out.println("in Task.run(), phase: " + phaser.getPhase() + ", id: " + this.id);  
  30.         }  
  31.     }  
  32. }  

   以上例子中,由于线程是在一个循环中start,因此start的时机有一定的间隔。本例中这些线程实际开始工作的时机是在

所有的线程都调用了phaser.arriveAndAwaitAdvance()之后。

     此外,如果留心arriveAndAwaitAdvance()方法的签名,会发现它并没有抛出InterruptedException,实际上,即使 当前线

程被中断,arriveAndAwaitAdvance()方法也不会返回,而是继续等待。如果在等待时希望可中断,或者可超时,那么需要使

用以下 方法:

Java代码   收藏代码
  1. awaitAdvance(arrive())  // 等效于arriveAndAwaitAdvance()  
  2. awaitAdvanceInterruptibly(int phase)  
  3. awaitAdvanceInterruptibly(int phase, long timeout, TimeUnit unit)  
 

3.2 Sample 2

    有些时候我们希望只有在某些外部条件满足时,才真正开始任务的执行,例如:

Java代码   收藏代码
  1. import java.io.BufferedReader;  
  2. import java.io.InputStreamReader;  
  3. import java.util.concurrent.Phaser;  
  4.   
  5. public class PhaserTest2 {  
  6.   
  7.     public static void main(String args[]) throws Exception {  
  8.         //  
  9.         final Phaser phaser = new Phaser(1);  
  10.         for(int i = 0; i < 5; i++) {  
  11.             phaser.register();  
  12.             System.out.println("starting thread, id: " + i);  
  13.             final Thread thread = new Thread(new Task(i, phaser));  
  14.             thread.start();  
  15.         }  
  16.           
  17.         //  
  18.         System.out.println("Press ENTER to continue");  
  19.         BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));  
  20.         reader.readLine();  
  21.         phaser.arriveAndDeregister();  
  22.     }  
  23.       
  24.     public static class Task implements Runnable {  
  25.         //  
  26.         private final int id;  
  27.         private final Phaser phaser;  
  28.   
  29.         public Task(int id, Phaser phaser) {  
  30.             this.id = id;  
  31.             this.phaser = phaser;  
  32.         }  
  33.           
  34.         @Override  
  35.         public void run() {  
  36.             phaser.arriveAndAwaitAdvance();  
  37.             System.out.println("in Task.run(), phase: " + phaser.getPhase() + ", id: " + this.id);  
  38.         }  
  39.     }  
  40. }  

    以上例子中,只有当用户按下回车之后,任务才真正开始执行。需要注意的是,arriveAndDeregister()方法不会被阻塞,并且

返回到达时的phase number(arrive方法也是如此)。

 

3.3 Sample 3

    CyclicBarrier支持barrier action, Phaser同样也支持。不同之处是Phaser的barrier action需要改写onAdvance方法来进行定制。

Java代码   收藏代码
  1. import java.util.concurrent.Phaser;  
  2.   
  3. public class PhaserTest3 {  
  4.   
  5.     public static void main(String args[]) throws Exception {  
  6.         //  
  7.         final int count = 5;  
  8.         final int phaseToTerminate = 3;  
  9.         final Phaser phaser = new Phaser(count) {  
  10.             @Override  
  11.             protected boolean onAdvance(int phase, int registeredParties) {  
  12.                 System.out.println("====== " + phase + " ======");  
  13.                 return phase >= phaseToTerminate || registeredParties == 0;  
  14.             }  
  15.         };  
  16.           
  17.         //  
  18.         for(int i = 0; i < count; i++) {  
  19.             System.out.println("starting thread, id: " + i);  
  20.             final Thread thread = new Thread(new Task(i, phaser));  
  21.             thread.start();  
  22.         }  
  23.     }  
  24.       
  25.     public static class Task implements Runnable {  
  26.         //  
  27.         private final int id;  
  28.         private final Phaser phaser;  
  29.   
  30.         public Task(int id, Phaser phaser) {  
  31.             this.id = id;  
  32.             this.phaser = phaser;  
  33.         }  
  34.           
  35.         @Override  
  36.         public void run() {  
  37.             do {  
  38.                 try {  
  39.                     Thread.sleep(500);  
  40.                 } catch(InterruptedException e) {  
  41.                     // NOP  
  42.                 }  
  43.                 System.out.println("in Task.run(), phase: " + phaser.getPhase() + ", id: " + this.id);  
  44.                 phaser.arriveAndAwaitAdvance();  
  45.             } while(!phaser.isTerminated());  
  46.         }  
  47.     }  
  48. }  

   本例中的barrier action只是简单地打印了一条信息,此外在超过指定的迭代次数后终止了Phaser。

3.4 Sample 4

    在Smaple 3的例子中,主线程在其它工作线程结束之前已经终止。如果希望主线程等待这些工作线程结束,除了使用Thread.join()之外,也可以尝试以下的方式:

Java代码   收藏代码
  1. import java.util.concurrent.Phaser;  
  2.   
  3. public class PhaserTest4 {  
  4.   
  5.     public static void main(String args[]) throws Exception {  
  6.         //  
  7.         final int count = 5;  
  8.         final int phaseToTerminate = 3;  
  9.         final Phaser phaser = new Phaser(count) {  
  10.             @Override  
  11.             protected boolean onAdvance(int phase, int registeredParties) {  
  12.                 System.out.println("====== " + phase + " ======");  
  13.                 return phase == phaseToTerminate || registeredParties == 0;  
  14.             }  
  15.         };  
  16.           
  17.         //  
  18.         for(int i = 0; i < count; i++) {  
  19.             System.out.println("starting thread, id: " + i);  
  20.             final Thread thread = new Thread(new Task(i, phaser));  
  21.             thread.start();  
  22.         }  
  23.           
  24.         //  
  25.         phaser.register();  
  26.         while (!phaser.isTerminated()) {  
  27.             phaser.arriveAndAwaitAdvance();  
  28.         }  
  29.         System.out.println("done");  
  30.     }  
  31.       
  32.     public static class Task implements Runnable {  
  33.         //  
  34.         private final int id;  
  35.         private final Phaser phaser;  
  36.   
  37.         public Task(int id, Phaser phaser) {  
  38.             this.id = id;  
  39.             this.phaser = phaser;  
  40.         }  
  41.           
  42.         @Override  
  43.         public void run() {  
  44.             while(!phaser.isTerminated()) {  
  45.                 try {  
  46.                     Thread.sleep(500);  
  47.                 } catch(InterruptedException e) {  
  48.                     // NOP  
  49.                 }  
  50.                 System.out.println("in Task.run(), phase: " + phaser.getPhase() + ", id: " + this.id);  
  51.                 phaser.arriveAndAwaitAdvance();  
  52.             }  
  53.         }  
  54.     }  
  55. }  

   如果希望主线程在特定的phase结束之后终止,那么可以在主线程中调用下述方法:

Java代码   收藏代码
  1. public static void awaitPhase(Phaser phaser, int phase) {  
  2.     int p = phaser.register(); // assumes caller not already registered  
  3.     while (p < phase) {  
  4.         if (phaser.isTerminated()) {  
  5.             break// ... deal with unexpected termination  
  6.         } else {  
  7.             p = phaser.arriveAndAwaitAdvance();  
  8.         }  
  9.     }  
  10.     phaser.arriveAndDeregister();  
  11. }  

    需要注意的是,awaitPhase方法中的if (phaser.isTerminated()) 分支里需要能够正确处理Phaser终止的情况。否则由于

在Phaser终止之后, phaser.register()和arriveAndAwaitAdvance()方法均返回负值,那么上述方法可能陷入死循环。

3.5 Sample 5

    以下对Phaser进行分层的例子:

Java代码   收藏代码
  1. import java.util.concurrent.Phaser;  
  2.   
  3. public class PhaserTest6 {  
  4.     //  
  5.     private static final int TASKS_PER_PHASER = 4;  
  6.   
  7.     public static void main(String args[]) throws Exception {  
  8.         //  
  9.         final int phaseToTerminate = 3;  
  10.         final Phaser phaser = new Phaser() {  
  11.             @Override  
  12.             protected boolean onAdvance(int phase, int registeredParties) {  
  13.                 System.out.println("====== " + phase + " ======");  
  14.                 return phase == phaseToTerminate || registeredParties == 0;  
  15.             }  
  16.         };  
  17.           
  18.         //  
  19.         final Task tasks[] = new Task[10];  
  20.         build(tasks, 0, tasks.length, phaser);  
  21.         for (int i = 0; i < tasks.length; i++) {  
  22.             System.out.println("starting thread, id: " + i);  
  23.             final Thread thread = new Thread(tasks[i]);  
  24.             thread.start();  
  25.         }  
  26.     }  
  27.   
  28.     public static void build(Task[] tasks, int lo, int hi, Phaser ph) {  
  29.         if (hi - lo > TASKS_PER_PHASER) {  
  30.             for (int i = lo; i < hi; i += TASKS_PER_PHASER) {  
  31.                 int j = Math.min(i + TASKS_PER_PHASER, hi);  
  32.                 build(tasks, i, j, new Phaser(ph));  
  33.             }  
  34.         } else {  
  35.             for (int i = lo; i < hi; ++i)  
  36.                 tasks[i] = new Task(i, ph);  
  37.         }  
  38.     }  
  39.   
  40.     public static class Task implements Runnable {  
  41.         //  
  42.         private final int id;  
  43.         private final Phaser phaser;  
  44.   
  45.         public Task(int id, Phaser phaser) {  
  46.             this.id = id;  
  47.             this.phaser = phaser;  
  48.             this.phaser.register();  
  49.         }  
  50.   
  51.         @Override  
  52.         public void run() {  
  53.             while (!phaser.isTerminated()) {  
  54.                 try {  
  55.                     Thread.sleep(200);  
  56.                 } catch (InterruptedException e) {  
  57.                     // NOP  
  58.                 }  
  59.                 System.out.println("in Task.run(), phase: " + phaser.getPhase()    + ", id: " + this.id);  
  60.                 phaser.arriveAndAwaitAdvance();  
  61.             }  
  62.         }  
  63.     }  
  64. }  

    需要注意的是,TASKS_PER_PHASER的值取决于具体的Task实现。对于Task执行时间很短的场景(也就是竞争相对激烈)

,可以考虑使用较小的TASKS_PER_PHASER值,例如4。反之可以适当增大TASKS_PER_PHASER。

猜你喜欢

转载自blog.csdn.net/luckykapok918/article/details/80983857