多线程编程是开发高并发应用的重点和难点,是许多互联网公司面试环节必不可少的部分.打算围绕多线程编程总结一些核心概念及它们之间的关系,本篇是Phaser.
Phaser
简介
Phaser是java7中引入的,属于线程同步辅助工具类.有一类场景,例如比赛,一个比赛分为3个阶段(phase): 初赛、复赛和决赛,现在规定只要所有运动员完成上一个阶段的比赛就可以进行下一阶段的比赛,并且比赛的过程中允许退赛(deregister),这个场景就很适合Phaser.
phaser和phase
正如其名,Phaser可以把一组线程的执行分为多个阶段(phase),并在每个阶段实现线程同步,而且每个阶段可以减少或者增加线程.概言之,一个phaser可以包含一个或者多个phase.
party
通过phaser同步的线程被称为party.这个party并没有特别的含义,只是Oracle官方的命名.所有需要同步的party必须持有同一个phaser对象.party需要向phaser注册,执行phaser.register()
方法注册,该方法仅仅是增加phaser中的线程计数.也可以通过构造器注册,比如new Phaser(5)
就会在创建phaser对象时注册5个party,随后这5个party只要持有该phaser对象并调用该对象的api就能实现同步.
arrived和unarrived
party到达一个phaser某个阶段之前处于unarrived状态,到达时处于arrived状态.一个arrived的party也被称为arrival.
deregister
一个线程可以在到达(arrive)某个阶段(phase)后退出(deregister),此时可以使用arriveAndDeregister()方法.
phase计数
Phaser类有一个phase计数,初始阶段为0.当一个阶段的所有线程到达(arrive)时,会将phase计数加1,这个动作被称为advance.当这个计数达到Integer.MAX_VALUE时,会被重置为0,开始下一轮循环,不过相信很多程序不可能达到这个值.advace这个词出现在Phaser类的很多api里,比如arriveAndAwaitAdvance()、awaitAdvance(int phase)等.在advance过程中,会触发onAdvance(int phase, int registeredParties)方法的执行.
onAdvance(int phase, int registeredParties)
可以在这个方法中定义advance过程中需要执行何种操作,如果进入下一阶段(phase)执行,返回false.如果返回true,会导致phaser结束,因此该方法也是终止phaser的关键所在.
Tiering
Tiering即分层的意思.Phaser支持分层结构,即通过构造函数Phaser(Phaser parent)和Phaser(Phaser parent, int parties)构造一个树形结构。这有助于减轻因在单个的Phaser上注册过多的任务而导致的竞争,从而提升吞吐量,代价是增加单个操作的开销。
api
主要有以下api:
-
arrive(): 通知phaser该线程已到达,并且不需等待其它线程,直接进入下一个执行阶段(phase)
-
arriveAndAwaitAdvance(): 通知phaser该线程已到达,并且等待其它线程.
-
arriveAndDeregister()
-
awaitAdvance(int phase): 阻塞线程,直到phaser的phase计数从参数中的phase变化成为另一个值.比如
awaitAdvance(2)
,会导致线程阻塞,直到phaser的phase计数变为3以后才会继续执行. -
awaitAdvanceInterruptibly(int phase)
-
onAdvance(int phase, int registeredParties)
-
register(): phaser的线程计数加1.
使用案例
案例介绍
6个游泳选手完成一场比赛需要分为3个阶段: 到达赛场、准备、完成比赛
案例代码
主要分为3个类: MyPhaser.java、Swimmer.java、SwimmerTest.java.
MyPhaser.java代码如下:
package com.ms.thread.phaser;
import java.util.concurrent.Phaser;
/**
* 自定义Phaser类,需要重写onAdvance方法
* @author chenxin
* @since 2018-04-20
*/
public class MyPhaser extends Phaser{
//定义结束阶段.这里是完成3个阶段以后结束
private int phaseToTerminate = 2;
@Override
protected boolean onAdvance(int phase, int registeredParties) {
System.out.println("*第"+phase+"阶段完成*");
//到达结束阶段,或者还没到结束阶段但是party为0,都返回true,结束phaser
return phase==phaseToTerminate || registeredParties==0;
}
}
Swimmer.java代码如下:
package com.ms.thread.phaser;
import java.util.concurrent.Phaser;
/**
* 游泳者线程.需要持有phaser对象
* @author chenxin
* @since 2018-04-20
*/
public class Swimmer implements Runnable{
private Phaser phaser;
public Swimmer(Phaser phaser) {
this.phaser = phaser;
}
@Override
public void run() {
/*
* 从这里到第一个phaser.arriveAndAwaitAdvance()是第一阶段做的事
*/
System.out.println("游泳选手-"+Thread.currentThread().getName()+":已到达赛场");
phaser.arriveAndAwaitAdvance();
/*
* 从这里到第二个phaser.arriveAndAwaitAdvance()是第二阶段做的事
*/
System.out.println("游泳选手-"+Thread.currentThread().getName()+":已准备好");
phaser.arriveAndAwaitAdvance();
/*
* 从这里到第三个phaser.arriveAndAwaitAdvance()是第三阶段做的事
*/
System.out.println("游泳选手-"+Thread.currentThread().getName()+":完成比赛");
phaser.arriveAndAwaitAdvance();
}
}
SwimmerTest.java代码如下:
package com.ms.thread.phaser;
/**
* 用于测试MyPhaser和Swimmer类
* @author chenxin
* @since 2018-04-20
*/
public class SwimmerTest {
//游泳选手个数
private static int swimmerNum = 6;
public static void main(String[] args) {
MyPhaser phaser = new MyPhaser();
//注册主线程,用于控制phaser何时开始第二阶段
phaser.register();
for(int i=0; i<swimmerNum; i++) {
phaser.register();
new Thread(new Swimmer(phaser),"swimmer"+i).start();
}
/*
* 此时某个游泳者所在的线程都已经完成了第一阶段,但是没法进入第二阶段,因为主线程还没到达第一阶段
*/
//主线程到达第一阶段并且不参与后续阶段.其它线程从此时可以进入后面的阶段.
phaser.arriveAndDeregister();
//加while是为了防止其它线程没结束就打印了"比赛结束”
while (!phaser.isTerminated()) {
}
System.out.println("===== 比赛结束 =====");
}
}
案例执行结果
打印的结果如下:
游泳选手-swimmer0:已到达赛场
游泳选手-swimmer2:已到达赛场
游泳选手-swimmer1:已到达赛场
游泳选手-swimmer3:已到达赛场
游泳选手-swimmer5:已到达赛场
游泳选手-swimmer4:已到达赛场
*第0阶段完成*
游泳选手-swimmer4:已准备好
游泳选手-swimmer3:已准备好
游泳选手-swimmer2:已准备好
游泳选手-swimmer5:已准备好
游泳选手-swimmer1:已准备好
游泳选手-swimmer0:已准备好
*第1阶段完成*
游泳选手-swimmer0:完成比赛
游泳选手-swimmer2:完成比赛
游泳选手-swimmer3:完成比赛
游泳选手-swimmer1:完成比赛
游泳选手-swimmer4:完成比赛
游泳选手-swimmer5:完成比赛
*第2阶段完成*
===== 比赛结束 =====
网友评论