一、Phaser简介
Phaser
是JDK1.7开始引入的一个同步工具类,适用于一些需要分阶段的任务的处理。它的功能与 CyclicBarrier和CountDownLatch有些类似,类似于一个多阶段的栅栏,并且功能更强大,我们来比较下这三者的功能:
同步器 | 作用 |
---|---|
CountDownLatch | 倒数计数器,初始时设定计数器值,线程可以在计数器上等待,当计数器值归0后,所有等待的线程继续执行 |
CyclicBarrier | 循环栅栏,初始时设定参与线程数,当线程到达栅栏后,会等待其它线程的到达,当到达栅栏的总数满足指定数后,所有等待的线程继续执行 |
Phaser | 多阶段栅栏,可以在初始时设定参与线程数,也可以中途注册/注销参与者,当到达的参与者数量满足栅栏设定的数量后,会进行阶段升级(advance) |
Phaser中有一些比较重要的概念,理解了这些概念才能理解Phaser的功能。
phase(阶段)
我们知道,在CyclicBarrier中,只有一个栅栏,线程在到达栅栏后会等待其它线程的到达。
Phaser也有栅栏,在Phaser中,栅栏的名称叫做phase(阶段),在任意时间点,Phaser只处于某一个phase(阶段),初始阶段为0,最大达到Integerr.MAX_VALUE
,然后再次归零。当所有parties参与者都到达后,phase值会递增。
如果看过之前关于CyclicBarrier的文章,就会知道,Phaser中的phase(阶段)这个概念其实和CyclicBarrier中的Generation很相似,只不过Generation没有计数。
parties(参与者)
parties(参与者)其实就是CyclicBarrier中的参与线程的概念。
CyclicBarrier中的参与者在初始构造指定后就不能变更,而Phaser既可以在初始构造时指定参与者的数量,也可以中途通过register
、bulkRegister
、arriveAndDeregister
等方法注册/注销参与者。
arrive(到达) / advance(进阶)
Phaser注册完parties(参与者)之后,参与者的初始状态是unarrived的,当参与者到达(arrive)当前阶段(phase)后,状态就会变成arrived。当阶段的到达参与者数满足条件后(注册的数量等于到达的数量),阶段就会发生进阶(advance)——也就是phase值+1。
Termination(终止)
代表当前Phaser对象达到终止状态,有点类似于CyclicBarrier中的栅栏被破坏的概念。
Tiering(分层)
Phaser支持分层(Tiering) —— 一种树形结构,通过构造函数可以指定当前待构造的Phaser对象的父结点。之所以引入Tiering,是因为当一个Phaser有大量参与者(parties)的时候,内部的同步操作会使性能急剧下降,而分层可以降低竞争,从而减小因同步导致的额外开销。
在一个分层Phasers的树结构中,注册和撤销子Phaser或父Phaser是自动被管理的。当一个Phaser的参与者(parties)数量变成0时,如果有该Phaser有父结点,就会将它从父结点中溢移除。
关于Phaser的分层,后续我们在讲Phaser原理时会进一步讨论。
二、Phaser示例
为了更好的理解Phaser的功能,我们来看几个示例:
示例一
通过Phaser控制多个线程的执行时机:有时候我们希望所有线程到达指定点后再同时开始执行,我们可以利用 CyclicBarrier或 CountDownLatch来实现,这里给出使用Phaser的版本。
public class PhaserTest1 {
public static void main(String[] args) {
Phaser phaser = new Phaser();
for (int i = 0; i < 10; i++) {
phaser.register(); // 注册各个参与者线程
new Thread(new Task(phaser), "Thread-" + i).start();
}
}
}
class Task implements Runnable {
private final Phaser phaser;
Task(Phaser phaser) {
this.phaser = phaser;
}
@Override
public void run() {
int i = phaser.arriveAndAwaitAdvance(); // 等待其它参与者线程到达
// do something
System.out.println(Thread.currentThread().getName() + ": 执行完任务,当前phase =" + i + "");
}
}
输出:
Thread-8: 执行完任务,当前phase =1
Thread-4: 执行完任务,当前phase =1
Thread-3: 执行完任务,当前phase =1
Thread-0: 执行完任务,当前phase =1
Thread-5: 执行完任务,当前phase =1
Thread-6: 执行完任务,当前phase =1
Thread-7: 执行完任务,当前phase =1
Thread-9: 执行完任务,当前phase =1
Thread-1: 执行完任务,当前phase =1
Thread-2: 执行完任务,当前phase =1
以上示例中,创建了10个线程,并通过register
方法注册Phaser的参与者数量为10。当某个线程调用arriveAndAwaitAdvance
方法后,arrive数量会加1,如果数量没有满足总数(参与者数量10),当前线程就是一直等待,当最后一个线程到达后,所有线程都会继续往下执行。
注意:
arriveAndAwaitAdvance
方法是不响应中断的,也就是说即使当前线程被中断,
arriveAndAwaitAdvance方法