onAdvance()是Phaser类提供的方法,用来在每次执行时phaser变化阶段。它接收两个参数:当前阶段的数量和注册参与者的数量。如果Phaser继续执行则返回布尔值false,或者Phaser已经结束并且必须进入终止状态时返回true。
如果注册参与者的数量为零,此方法的默认实现返回true,否则为false。但是可以通过继承Phaser类以及重写此方法修改这个特性。正常情况下,当需要执行一些活动从一个阶段到下一个阶段时,重写此方法会很有趣。
在本节中,通过重写Phaser类的onAdvance()方法在每个阶段变化中执行活动,学习如何控制阶段中的变化。本范例模拟一场考试,学生需要完成三个测试,所有学生必须完成一个测试之后才能进行下一个。
准备工作
本范例通过Eclipse开发工具实现。如果使用诸如NetBeans的开发工具,打开并创建一个新的Java项目。
实现过程
通过如下步骤完成范例:
-
创建名为MyPhaser的类,继承Phaser类:
public class MyPhaser extends Phaser{
-
重写onAdvance()方法。根据phase属性值,掉哦那个不同的附属方法。如果phase属性值等于0,调用studentsArrived()方法。如果phase属性值等于1,调用finishFirstExercise()方法。如果phase属性值等于2,调用finishSecondExercise()方法。最后,如果phase属性值等于3,调用finishExam()方法。否则,返回true表明phaser已终止:
@Override protected boolean onAdvance(int phase, int registeredParties) { switch (phase) { case 0: return studentsArrived(); case 1: return finishFirstExercise(); case 2: return finishSecondExercise(); case 3: return finishExam(); default: return true; } }
-
实现附属方法studentsArrived(),输出两条日志信息到控制台并且返回false表明phaser一直在执行:
private boolean studentsArrived() { System.out.printf("Phaser : The exam are going to start. The students are ready.\n"); System.out.printf("Phaser : We have %d students.\n", getRegisteredParties()); return false; }
-
实现附属方法finishFirstExercise(),输出两条信息到控制台并且返回false表明phaser一直在执行:
private boolean finishFirstExercise() { System.out.printf("Phaser : All the students have finished the first exercise.\n"); System.out.printf("Phaser : It's time for the second one.\n"); return false; }
-
实现附属方法finishSecondExercise(),输出两条信息到控制台并且返回false表明phaser一直在执行:
private boolean finishSecondExercise() { System.out.printf("Phaser : All the students have finished the second exercise.\n"); System.out.printf("Phaser : It's time for the third one.\n"); return false; }
-
实现附属方法finishExam(),输出两条信息到控制台并且返回true表明phaser已经完成工作:
private boolean finishExam() { System.out.printf("Phaser : All the students have finished the exam.\n"); System.out.printf("Phaser : Thank you for your time.\n"); return false; }
-
创建名为Student的类实现其Runnable接口,此类用来模拟考试中的学生:
public class Student implements Runnable {
-
定义名为phaser的Phaser属性:
private Phaser phaser;
-
实现类构造函数,初始化Phaser对象:
public Student(Phaser phaser) { this.phaser = phaser; }
-
实现run()方法,模拟考试实现:
@Override public void run() {
-
首先,方法输出指明一个学生已经进入考场的信息到控制台,并且调用phaser的arrvieAndAwaitAdvance()方法等待其它线程:
System.out.printf("%s : Has arrived to do the exam. %s\n", Thread.currentThread().getName(), new Date()); phaser.arriveAndAwaitAdvance();
-
然后,输出信息到控制台,调用doExercise1()方法模拟考试第一个测试的实现。接下来,输出另一条信息到控制台,调用phaser的arrvieAndAwaitAdvance()方法等待完成第一个测试的其他学生:
System.out.printf("%s : Is going to do the first exercise. %s\n", Thread.currentThread().getName(), new Date()); doExercise1(); System.out.printf("%s : Has done the first exercise. %s\n", Thread.currentThread().getName(), new Date()); phaser.arriveAndAwaitAdvance();
-
对第二、三个测试实现相同的代码:
System.out.printf("%s : Is going to do the second exercise. %s\n", Thread.currentThread().getName(), new Date()); doExercise2(); System.out.printf("%s : Has done the second exercise. %s\n", Thread.currentThread().getName(), new Date()); phaser.arriveAndAwaitAdvance(); System.out.printf("%s : Is going to do the third exercise. %s\n", Thread.currentThread().getName(), new Date()); doExercise3(); System.out.printf("%s : Has finished the exam. %s\n", Thread.currentThread().getName(), new Date()); phaser.arriveAndAwaitAdvance();
-
实现附属方法doExercise1(),方法设置当前线程或者执行此方法的线程随机休眠一段时间:
private void doExercise1() { try { long duration = (long)(Math.random() * 10); TimeUnit.SECONDS.sleep(duration); } catch (InterruptedException e) { e.printStackTrace(); } }
-
实现附属方法doExercise2(),方法设置当前线程或者执行此方法的线程随机休眠一段时间:
private void doExercise2() { try { long duration = (long)(Math.random() * 10); TimeUnit.SECONDS.sleep(duration); } catch (InterruptedException e) { e.printStackTrace(); } }
-
实现附属方法doExercise3(),方法设置当前线程或者执行此方法的线程随机休眠一段时间:
private void doExercise3() { try { long duration = (long)(Math.random() * 10); TimeUnit.SECONDS.sleep(duration); } catch (InterruptedException e) { e.printStackTrace(); } }
-
实现范例的主类,创建一个包含main()方法的Main类:
public class Main { public static void main(String[] args) {
-
创建MyPhaser对象:
MyPhaser phaser = new MyPhaser();
-
创建五个Student对象,使用register()方法注册到phaser属性中:
Student students[] = new Student[5]; for ( int i = 0 ; i < students.length ; i ++) { students[i] = new Student(phaser); phaser.register(); }
-
创建五个线程,开始执行:
Thread threads[] = new Thread[students.length]; for ( int i = 0 ; i < students.length ; i ++) { threads[i] = new Thread(students[i], "Student " + i); threads[i].start(); }
-
等待五个线程结束:
for ( int i = 0 ; i < students.length ; i ++) { try { threads[i].join(); } catch (InterruptedException e) { e.printStackTrace(); } }
-
使用isTerminated()方法,输出展示phaser处于终止状态的信息到控制台:
System.out.printf("Main : The phaser has finished : %s.\n", phaser.isTerminated());
工作原理
本范例模拟包括三个测试的考试实现。所有学生必须现完成一个测试才能开始下一个。为实现并发要求,我们使用Phaser类,然而这种情况下,需要通过继承原始类重写onAdvance()方法,定制phaser对象。
在阶段变化以及唤醒arrvieAndAwaitAdvance()方法中休眠的所有线程之前,Phaser调用此方法。结束一个阶段的最后一个线程调用此方法,以此作为arrvieAndAwaitAdvance()方法的部分代码。这个方法传递实际阶段的数量作为-0表示第一个阶段,以及注册参与者的数量为参数。实际阶段是最有用的参数,如果依赖实际阶段执行不同的操作,需要使用选择结构(if…else或则switch)确定想要执行的操作。本范例中,我们使用switch结构为每个阶段变化选择不同的方法。
onAdvance()方法返回表明phaser是否终止的布尔值,如果phaser返回false,表示它未终止,线程将继续其他阶段的执行。如果phaser返回true,phaser依然唤醒等待线程,但将phaser置为终止状态。借此未来所有被调用的phaser方法将立即返回,isTerminated()方法返回true。
在Main类中,当创建Phaser对象时,不用明确阶段中的参与者数量,为每个Student对象调用register()方法即可注册成phaser中的参与者。Student对象或者执行对象的线程并没有因为方法调用和phaser建立关联,阶段的参与者数量只是一个数字,phaser和参与者之间没有联系。
下图显示本范例在控制台输出的执行结果:
可以看到学生在不同的时间内完成第一个测试,当所有学生结束第一个测试时,phaser调用onAdvance()方法在控制台输出信息,然后所有学生在同时开始第二个测试。
更多关注
- 本章“运行阶段并发任务”小节。
- 第九章“测试并发应用”中的“监控Phaser类”小节。