-
题目
5个学生一起参加考试,一共有三道题,要求所有学生到齐才能开始考试,全部同学都做完第一题,学生才能继续做第二题,全部学生做完了第二题,才能做第三题,所有学生都做完的第三题,考试才结束。
-
题目分析
这是一个多线程(5个学生)分阶段问题(考试考试、第一题做完、第二题做完、第三题做完),所以很适合用Phaser解决这个问题。
-
实例
public class PhaserTest {
public static void main(String[] args) throws InterruptedException, ExecutionException
{
MyPhaser phaser = new MyPhaser();
ExecutorService executorService = Executors.newCachedThreadPool();
CompletionService<String> completionService = new ExecutorCompletionService<String>(
executorService);
int num=5;
System.out.println("同学们准备考试了!");
for(int i=0;i<num;i++)
{
completionService.submit(getTask(phaser,4));
}
String temp= null;
for(int j=0;j<num;j++){
temp = completionService.take().get();
System.out.println(temp + "\t");
}
phaser.forceTermination();
executorService.shutdown();
}
public static Callable<String> getTask(Phaser phaser,final int phases){
phaser.register();
return new Callable<String>() {
int time = 0;
int sum=0;
String action=null;
@Override
public String call() throws Exception {
try {
for(int i=0;i<phases;i++)
{
switch (i) {
case 0:
action="到达考场";
active();
break;
case 1:
action="做第一道题";
active();
break;
case 2:
action="做第二道题";
active();
break;
case 3:
action="做第三道题";
active();
break;
default:
break;
}
}
} catch (InterruptedException e) {
log.error(e.getMessage());
}
return Thread.currentThread().getName()+"答题时间"+sum;
}
void active() throws InterruptedException
{
time = new Random().nextInt(10);
sum+=time;
System.out.println(Thread.currentThread().getName()+action+"需要"+time+"s");
Thread.sleep(time*1000);
System.out.println(Thread.currentThread().getName()+action+"完成");
phaser.arriveAndAwaitAdvance();
}
};
}
}
class MyPhaser extends Phaser {
@Override
protected boolean onAdvance(int phase, int registeredParties) {
// TODO Auto-generated method stub
switch (phase) {
case 0:
System.out.println("所有同学到达考场,开始答第一题");
break;
case 1:
System.out.println("所有同学第一题答完,开始答第二题");
break;
case 2:
System.out.println("所有同学第二题答完,开始答第三题");
break;
case 3:
System.out.println("所有同学第三题答完,考试结束");
break;
default:
break;
}
return super.onAdvance(phase, registeredParties);
}
}
-
Phaser特点
-
Phaser可以通过register和bulkRegister方法动态增加注册任务的数量,此外也支持通过其构造函数进行指定初始数量。相对的,Phaser也可以通过arriveAndDeregister方法减少注册任务的数量。之前的CyclicBarrier和CountDownLatch,注册的任务数量都必须确定,不能改变。
-
Phaser可以主动进行终止。被终止之后,Phaser无法调整注册任务的数量。正在等待中的任务会立刻执行。CyclicBarrier和CountDownLatch没有终止的功能。可以通过重写onAdvance方法和使用forceTermination实现Phaser的终止isTerminated方法可以检测phaser是否被终止,如果被终止,就返回true。
-
Phaser可以分层,如果在同一Phaser注册的线程过多,竞争的开销将会很大。如果对Phaser进行分层,将有效减少竞争的开销,提高吞吐量。但是相应的,单个操作的开销将会变高(如果存在Phaser分层结构,那么父子Phaser之间就需要相互协调,所以单个操作的开销变高)。
Phaser phaser = new Phaser(); Phaser childPhaser = new Phaser(phaser);
-
知识点
- 在Phaser内有2个重要状态,分别是phase和party。phase就是阶段,初值为0,当所有的线程执行完本轮任务,同时开始下一轮任务时,意味着当前阶段已结束,进入到下一阶段,phase的值自动加1。
- 使用forceTermination方法,phaser将立即终止,所有被阻塞的线程将会继续执行。
- Phaser可以通过register和bulkRegister(批量注册)方法动态增加注册任务的数量,此外也支持通过其构造函数进行指定初始数量。相对的,Phaser也可以通过arriveAndDeregister方法减少注册任务的数量。
- arrive 告诉phaser对象,当前线程已经执行到这里了,而且将继续执行下去,并且让phaser中的计数器+1。
- awaitAdvance 阻塞当前线程,等待当前阶段下其他所有的线程都到达。这个方法需要传入一个参数,如果这个参数和phaser当前周期相同,那么当前线程将进入等待状态。如果这个参数和phaser当前周期不同,那么当前线程立刻结束等待,继续执行。
- arriveAndAwaitAdvance 线程执行这个方法之后将会进入等待状态。直到phaser管理的所有线程执行了这个方法,phaser中的计数重置为0,阶段数+1,所有处于等待状态下的线程继续向下执行。