Java线程--Phaser阶段器

Phaser阶段器

目录

Phaser阶段器

Phaser概念 

Phaser示例1

Phaser示例2 

Phaser比CountDownLatch灵活 

Phaser比CyclicBarrier灵活

Phaser总结

一、简述

二、常用的Barrier比较

三、API


Phaser概念 

 Phaser表示“阶段器”,用来解决控制多个线程分阶段共同完成大型任务的情景问题。也就是说,事情一趟流程走下来,可以分为若干个阶段,每个阶段的工作都需要注册该阶段的N个人来干。每人干自己的活,手脚快的呢,就干好到达第一阶段的屏障前等待手脚慢的人干完活后也到达第一阶段屏障,...直至注册该阶段的N个人都各自干完了一阶段期间内的活,都到达了第一阶段屏障,第一阶段屏障打开,准许这N个人通行,继续进行下一阶段...周而复始...直至所有阶段都干完,结束。

其作用相比CountDownLatch和CyclicBarrier更加灵活。

Phaser内有2个重要状态:phaseparty,有一个重要方法:onAdvance()

  • phase阶段,初值为0,当所有的线程执行完本轮任务,phase值自动加1,phase放行,线程可进入到下一阶段。
  • party注册线程,party=3就意味着要在该阶段注册3个线程,Phaser对象当前管理着3个线程。
  • boolean onAdvance(int phase, int registeredParties)方法,经常需要被重载,此方法有2个作用:

当每一个阶段执行完毕,此方法会被调用,相当于CyclicBarrier的barrierAction。

当此方法返回true时,意味着Phaser被终止,因此重载此方法时将返回值是否设置为true来决定是否终止所有线程。


下面举例业务场景:

仓库来了一货车的货物要入库,货物很多,需要N个人(多线程)来干,干活有快有慢的,手脚快的人多劳多得

第一阶段:注册了3人,每人都从车上卸下货物,3人将所有的货物都卸货完毕后,得以通过第一阶段屏障,进入下一阶段

第二阶段:注册了3人,每人都把货物码放托盘,3人将所有的货物都码托完毕后,得以通过第二阶段屏障,进入下一阶段

第三阶段:注册了3人,每人都把码盘入仓存放,3人将所有的货物都入仓完毕后,得以通过第三阶段屏障,进入下一阶段

结束

Phaser

 Phaser示例1


import java.util.concurrent.*;
import java.util.concurrent.locks.*;
import java.util.concurrent.atomic.*;
 

//任务
class TaskWms implements Runnable
{
    private Phaser phase;
    public TaskWms(Phaser phase){
	this.phase = phase ;
    }
	
    public void run(){
		
	/**
	* 初始
	*/
	String tName = Thread.currentThread().getName();

	/**
	* 第0段:卸货
	*/
	try{
	    long time = (long)(Math.random() * 5000);
	    Thread.sleep(time); 
	    System.out.println(tName+"卸货】耗时is:["+time+"]");
	}catch(InterruptedException e){}

	phase.arriveAndAwaitAdvance();//每个线程都到达后,通过当前阶段,调用onAdvance()方法.

	/**
	* 第1段:码托
	*/
	try{
	    long time = (long)(Math.random() * 5000);
	    Thread.sleep(time); 
	    System.out.println(tName+"码托】耗时is:["+time+"]");
	}catch(InterruptedException e){}

	phase.arriveAndAwaitAdvance();

	/**
	* 第2段:入库
	*/
	try{
	    long time = (long)(Math.random() * 5000);
	    Thread.sleep(time); 
	    System.out.println(tName+"入库】耗时is:["+time+"]");
	}catch(InterruptedException e){}

	phase.arriveAndAwaitAdvance();

	/**
	* 结束:结账领工钱
	*/
	System.out.println(tName+"结账领工钱...");
	phase.arriveAndDeregister();
    }
}


/**
* 自定义阶段器:重载onAdvance()方法:可表达每阶段结束后的响应
* 注意Phaser阶段器是从零开始的,
* 首次调用onAdvance()时,刚刚越过的阶段屏障的值为0
*/
class MyPhaser extends Phaser
{
    public boolean onAdvance(int p, int party){
	switch(p){
	    case 0:
		System.out.println("...卸货完毕!\n\r");
		return false;
	    case 1:
		System.out.println("...码托完毕!\n\r");
		return false;
	    case 2:
		System.out.println("...入库完毕!\n\r");
		return false;
	    case 3:
		System.out.println("\n\r大家都已记账结算工资!\n\r");
		return true;
	    default:
		return true;
	}
    }
}


/**
* 测试
*/
public class PhaserTest {

    public static void main(String[] args) {

	MyPhaser phase = new MyPhaser();

	Thread[] threads = new Thread[3];
		
	for (int i = 0; i < 3; i++) {
	    TaskWms task = new TaskWms(phase);
	    phase.register();
	    threads[i] = new Thread(task, "T"+i);
	    threads[i].start();
	}

	for (int i = 0; i < 3; i++) {
	    try {
		threads[i].join();
	    } 
	    catch (InterruptedException e) {
	    }
	}
		
	System.out.println("阶段完毕:OK? Phaser has finished : "+phase.isTerminated());
    }
}

程序结果如下:

T1卸货】耗时is:[358]
T2卸货】耗时is:[448]
T0卸货】耗时is:[625]
...卸货完毕!

T0码托】耗时is:[38]
T1码托】耗时is:[1785]
T2码托】耗时is:[4536]
...码托完毕!

T1入库】耗时is:[414]
T0入库】耗时is:[797]
T2入库】耗时is:[2900]
...入库完毕!

T2结账领工钱...
T1结账领工钱...
T0结账领工钱...

大家都已记账结算工资!

阶段完毕:OK? Phaser has finished : true

Phaser示例2 

此示例是针对上述Phaser示例1的变种:

import java.util.concurrent.*;
import java.util.concurrent.locks.*;
import java.util.concurrent.atomic.*;
 

//任务:3人卸货

class TaskWms0 implements Runnable
{
    private Phaser phase;
    public TaskWms0(Phaser phase){
	this.phase = phase ;
    }

    public void run(){
	String tName = Thread.currentThread().getName();
		
	try{
	    long time = (long)(Math.random() * 500);
	    Thread.sleep(time); 
	    System.out.println(tName+"--卸货】耗时is:["+time+"]");
	}catch(InterruptedException e){}

	phase.arriveAndDeregister();//每个线程都到达后,通过当前阶段,调用onAdvance()方法,释放注册信息.
	}

}


//任务:1人码托

class TaskWms1 implements Runnable
{
    private Phaser phase;
    public TaskWms1(Phaser phase){
	this.phase = phase ;
    }

    public void run(){
	String tName = Thread.currentThread().getName();
		
	try{
	    long time = (long)(Math.random() * 500);
	    Thread.sleep(time); 
	    System.out.println(tName+"--码托】耗时is:["+time+"]");
	}catch(InterruptedException e){}
	    phase.arriveAndDeregister();
	}
}


//任务:2人入库

class TaskWms2 implements Runnable
{
    private Phaser phase;
    public TaskWms2(Phaser phase){
	this.phase = phase ;
    }

    public void run(){
	String tName = Thread.currentThread().getName();
	
	try{
	    long time = (long)(Math.random() * 500);
	    Thread.sleep(time); 
	    System.out.println(tName+"--入库】耗时is:["+time+"]");
	}catch(InterruptedException e){}

	phase.arriveAndDeregister();
    }
}



/**
* 自定义阶段器:重载onAdvance()方法:可表达每阶段结束后的响应
* 注意Phaser阶段器是从零开始的,
* 首次调用onAdvance()时,刚刚越过的阶段屏障的值为0
*/

class MyPhaser extends Phaser
{
	public MyPhaser(int parties){
	    super(parties);
	}

	public MyPhaser(){
	    super();
	}

    public boolean onAdvance(int p, int party){
	switch(p){
	    case 0:
	        System.out.println("阶段"+p+":卸货完毕!\n\r");
		return false;
	    case 1:
	        System.out.println("阶段"+p+":码托完毕!\n\r");
		return false;
	    case 2:
		System.out.println("阶段"+p+":入库完毕!\n\r");
		return true;
	    default:
		return true;
	}
    }
}

 

/**
* 测试
*/
public class PhaserTest {

    public static void main(String[] args) {

	MyPhaser phase = new MyPhaser(1);
	/**
	* 等价于:这么两句合在一起的意思
	* MyPhaser phase = new MyPhaser();
	* phase.register();
	*/
	/**
	* register()注册几次,
	* 就要出现几次arrive()或arriveAndAwaitAdvance()或arriveAndDeregister(),
	* 才能让屏障打开,阶段继续
	*/

	char c = 'G';

	/**
	* 卸货任务3人
	*/
	String phaseAfterStr = phase.getPhase()==0?"":"阶段屏障("+(phase.getPhase()-1)+")之后";
	String phaseBeforeStr = "【.任务执行中.】阶段屏障("+phase.getPhase()+")之前";
	System.out.println(phaseAfterStr+phaseBeforeStr);
	
	Thread[] threads0 = new Thread[3];
	for (int i = 0; i < 3; i++) {
	    TaskWms0 task = new TaskWms0(phase);
	    phase.register();
	    threads0[i] = new Thread(task, "卸"+(c++));
	    threads0[i].start();
	}

	phase.arriveAndAwaitAdvance(); //设置屏障,卸货完成,才往下继续执行


	/**
	* 码托任务1人
	*/
	phaseAfterStr = phase.getPhase()==0?"":"阶段屏障("+(phase.getPhase()-1)+")之后";
	phaseBeforeStr = "【.任务执行中.】阶段屏障("+phase.getPhase()+")之前";
	System.out.println(phaseAfterStr+phaseBeforeStr);

	Thread[] threads1 = new Thread[1];
	for (int i = 0; i < 1; i++) {
	    TaskWms1 task = new TaskWms1(phase);
	    phase.register();
	    threads1[i] = new Thread(task, "码"+(c++));
	    threads1[i].start();
	}

	phase.arriveAndAwaitAdvance(); //设置屏障,码托完成,才往下继续执行

	
	/**
	* 入库任务2人
	*/
	phaseAfterStr = phase.getPhase()==0?"":"阶段屏障("+(phase.getPhase()-1)+")之后";
	phaseBeforeStr = "【.任务执行中.】阶段屏障("+phase.getPhase()+")之前";
	System.out.println(phaseAfterStr+phaseBeforeStr);

	Thread[] threads2 = new Thread[2];
	for (int i = 0; i < 2; i++) {
	    TaskWms2 task = new TaskWms2(phase);
	    phase.register();
	    threads2[i] = new Thread(task, "库"+(c++));
	    threads2[i].start();
	}

	phase.arriveAndAwaitAdvance(); //设置屏障,入库完成,才往下继续执行


	System.out.println("阶段完毕:OK? Phaser has finished : "+phase.isTerminated());

    }
}

程序运行结果如下:

【.任务执行中.】阶段屏障(0)之前
卸I--卸货】耗时is:[151]
卸H--卸货】耗时is:[252]
卸G--卸货】耗时is:[300]
阶段0:卸货完毕!

阶段屏障(0)之后【.任务执行中.】阶段屏障(1)之前
码J--码托】耗时is:[181]
阶段1:码托完毕!

阶段屏障(1)之后【.任务执行中.】阶段屏障(2)之前
库K--入库】耗时is:[343]
库L--入库】耗时is:[361]
阶段2:入库完毕!

阶段完毕:OK? Phaser has finished : true


Phaser比CountDownLatch灵活 

以下示例:演示一个线程等待一组线程 

/**
* if you want change the number of parties, you should create a new instance.
*/ 


/**
* 倒计时器 CountDownLatch(int parties) 
*
*         初始化必须指定好线程个数,且以后无法修改
*         调用countDown()方法1次,递减1次,直至降为0,唤醒调用await()方法的所有线程
*         是一次性用品,用完就扔,无法再用
*         一个线程和一组线程之间的通讯:一个等一组:一线程调用awaitAdvance(0),组线程调用arrive()
*/

import java.util.concurrent.*;
import java.util.concurrent.locks.*;
import java.util.concurrent.atomic.*;

public class PhaserTest_5 {
    public static void main(String[] args) {
        
	/**
	* 类似:CountDownLatch(3)
	*/
	Phaser phaser = new Phaser(3);         
        
        System.out.println("main blocking...!\n\r");

	//3个任务
        for(int i = 0 ; i < 3 ; i++){
            Task_05 task = new Task_05(phaser);
            Thread thread = new Thread(task,"T" + i);
            thread.start();
        }
        
	/**
	* 类似:countDownLatch.await()
	*/
	phaser.awaitAdvance(0); 

	System.out.println("\n\rmain over OK!");
    }
    
    static class Task_05 implements Runnable{
        private final Phaser phaser;
        
        Task_05(Phaser phaser){
            this.phaser = phaser;
        }
        
        public void run() {
	    try{
		String tName = Thread.currentThread().getName();
		long time = (long)(Math.random() * 500);
		Thread.sleep(time);
		System.out.println(tName+"--执行任务】耗时is:["+time+"]");
	    }catch(Exception e){}			
            
	    /**
	    * 类似:countDownLatch.countDown()
	    */
	    phaser.arrive();
        }
    }
}

程序运行结果如下:

main blocking...!

T1--执行任务】耗时is:[88]
T2--执行任务】耗时is:[106]
T0--执行任务】耗时is:[373]

main over OK!


 以下示例:演示一组线程等待一个线程 

/**
* if you want change the number of parties, you should create a new instance.
*/ 


/**
* 倒计时器 CountDownLatch(int parties) 
*
*         初始化必须指定好线程个数,且以后无法修改
*         调用countDown()方法1次,递减1次,直至降为0,唤醒调用await()方法的所有线程
*         是一次性用品,用完就扔,无法再用
*         一个线程和一组线程之间的通讯:一组等一个:一线程调用arrive(),组线程调用awaitAdvance(0)
*/

import java.util.concurrent.*;
import java.util.concurrent.locks.*;
import java.util.concurrent.atomic.*;

public class PhaserTest_5 {
    public static void main(String[] args) {
        
	/**
	* 类似:CountDownLatch(1)
	*/
	Phaser phaser = new Phaser(1);         

	//3个任务
        for(int i = 0 ; i < 3 ; i++){
            Task_05 task = new Task_05(phaser);
            Thread thread = new Thread(task,"T" + i);
            thread.start();
        }
        
	try{
	    long time = (long)(Math.random() * 1000);
	    Thread.sleep(time);
	    System.out.println("单一线程】耗时is:["+time+"]\n\r");
	}catch(Exception e){}		

	/**
	* 类似:countDownLatch.countDown()
	*/
	phaser.arrive(); 
    }
    
    static class Task_05 implements Runnable{
        private final Phaser phaser;
        
        Task_05(Phaser phaser){
            this.phaser = phaser;
        }
        
        public void run() {
	    /**
	    * 类似:countDownLatch.await()
	    */
	    phaser.awaitAdvance(0);

	    String tName = Thread.currentThread().getName();
	    System.out.println("单一线程已到达,组内线程【"+tName+"】--我得以执行任务!");
        }
    }
}

程序运行结果如下:

单一线程】耗时is:[446]

单一线程已到达,组内线程【T0】--我得以执行任务!
单一线程已到达,组内线程【T1】--我得以执行任务!
单一线程已到达,组内线程【T2】--我得以执行任务!


 Phaser比CyclicBarrier灵活

 CyclicBarrier指定的线程参与个数,是初始化时定死的

Phaser既可以初始化的时候指定线程参与个数;也可以初始化时不指定,而由后续的register()方法来动态注册参与线程

/**
* if you want change the number of parties, you should create a new instance.
*/ 

/**
* 循环屏障 CyclicBarrier(int parties) 
*
*         初始化必须指定好线程个数,且以后无法修改,可以理解为一组线程,组内线程总个数为parties
*         调用await()方法1次,递减1次,直至降为0,唤醒调用await()方法的所有线程,恢复parties
*         是循环用品,可以反复使用
*         一组线程内的线程彼此等待
*
* CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
*/


/**
* 代码演示:用本文的Phaser示例1改造下,同理:他们可以相互改造
*
* Phaser类的arriveAndAwaitAdvance()方法 
* 功效雷同
* CyclicBarrier类的await()方法
*/


import java.util.concurrent.*;
import java.util.concurrent.locks.*;
import java.util.concurrent.atomic.*;
 

//任务
class TaskWms implements Runnable
{
    private CyclicBarrier barrier;
    public TaskWms(CyclicBarrier barrier){
	this.barrier = barrier ;
    }
	
    public void run(){
		
	/**
	* 初始
	*/
	String tName = Thread.currentThread().getName();

	/**
	* 第0段:卸货
	*/
	try{
	    long time = (long)(Math.random() * 5000);
	    Thread.sleep(time); 
	    System.out.println(tName+"卸货】耗时is:["+time+"]");
	    barrier.await();//每个线程都到达后,通过当前屏障,继续往下执行.
	}catch(Exception e){}

	

	/**
	* 第1段:码托
	*/
	try{
	    long time = (long)(Math.random() * 5000);
	    Thread.sleep(time); 
	    System.out.println(tName+"码托】耗时is:["+time+"]");
	    barrier.await();
	}catch(Exception e){}

	

	/**
	* 第2段:入库
	*/
	try{
	    long time = (long)(Math.random() * 5000);
	    Thread.sleep(time); 
	    System.out.println(tName+"入库】耗时is:["+time+"]");
	    barrier.await();
	}catch(Exception e){}


	/**
	* 结束:结账领工钱
	*/
	System.out.println(tName+"结账领工钱...");
    }
}


/**
* 测试
*/
public class PhaserTest {

    public static void main(String[] args) {

	CyclicBarrier barrier = new CyclicBarrier(3);

	Thread[] threads = new Thread[3];
		
	for (int i = 0; i < 3; i++) {
	    TaskWms task = new TaskWms(barrier);
	    threads[i] = new Thread(task, "T"+i);
	    threads[i].start();
	}

	for (int i = 0; i < 3; i++) {
	    try {
		threads[i].join();
	    } 
	    catch (InterruptedException e) {
	    }
	}
		
	System.out.println("完毕:OK");
    }
}

程序运行结果如下:

T1卸货】耗时is:[1702]
T2卸货】耗时is:[2234]
T0卸货】耗时is:[3078]
T1码托】耗时is:[2159]
T2码托】耗时is:[3881]
T0码托】耗时is:[4503]
T0入库】耗时is:[2541]
T1入库】耗时is:[3614]
T2入库】耗时is:[4827]
T2结账领工钱...
T1结账领工钱...
T0结账领工钱...
完毕:OK


Phaser总结 

再次引用别人比较好的一篇博客总结:Phaser总结

 在JAVA 1.7引入了一个新的并发API:Phaser,一个可重用的同步barrier。在此前,JAVA已经有CyclicBarrier、CountDownLatch这两种同步barrier,但是Phaser更加灵活,而且侧重于“阶段推进”。

一、简述

1、注册机制:

与其他barrier不同的是,Phaser中的“注册的同步者(parties)”会随时间而变化,Phaser可以通过构造器初始化parties个数,也可以在Phaser运行期间随时加入(register)新的parties,以及在运行期间注销(deregister)parties。运行时可以随时加入、注销parties,只会影响Phaser内部的计数器,它建立任何内部的bookkeeping(账本),因此task任务线程不能查询自己是否已经注册,当然你可以通过实现子类来达成这一设计要求。


//伪代码 

/**
* 每个阶段成对出现 register() 和 arrive()[或arriveAndAwaitAdvance()][或arriveAndDeregister()]
*/

Phaser phaser = new Phaser();  
phaser.register(); //parties count: 1  
....  
phaser.arriveAndDeregister(): //parties count : 0;  
.... 


/**
* register()方法不是必须的:有简略的注册机制,即初始化Phaser时,给出并发线程个数parties的值
*/

Phaser phaser = new Phaser(3);  
//phaser.register(); //不需要
//phaser.register(); //不需要
//phaser.register(); //不需要


此外,CyclicBarrier、CountDownLatch需要在初始化的构造函数中指定同步者的个数,且运行时无法再次调整。

//伪代码

/**
* if you want change the number of parties, you should create a new instance.
*/ 


/**
* 倒计时器 CountDownLatch(int parties) 
*
*         初始化必须指定好线程个数,且以后无法修改
*         调用countDown()方法1次,递减1次,直至降为0,唤醒调用await()方法的所有线程
*         是一次性用品,用完就扔,无法再用
*         一个线程和一组线程之间的通讯:一个等一组:一线程调用await(),组线程调用countDown()
*         一组线程和一个线程之间的通讯:一组等一个:组线程调用await(),一线程调用countDown()
*/
CountDownLatch countDownLatch = new CountDownLatch(12); 
 

/**
* 循环屏障 CyclicBarrier(int parties) 
*
*         初始化必须指定好线程个数,且以后无法修改,可以理解为一组线程,组内线程总个数为parties
*         调用await()方法1次,递减1次,直至降为0,唤醒调用await()方法的所有线程,恢复parties
*         是循环用品,可以反复使用
*         一组线程内的线程彼此等待
*/
CyclicBarrier cyclicBarrier = new CyclicBarrier(12);

2、同步机制:

类似于CyclicBarrier,arrivedAndAwaitAdvance()方法的效果类似于CyclicBarrier的await()。Phaser的每个周期(generation)都有一个phase数字,phase 从0开始,当所有的已注册的parties都到达后(arrive)将会导致此phase数字自增1,阶段向前推进。这个phase数字用于表示当前parties所处于的“阶段周期”,它既可以标记和控制parties的wait行为、唤醒等待的时机。


    2.1)Arrival:Phaser中的arrive()、arriveAndDeregister()方法,这两个方法不会阻塞(block),但是会返回相应的phase数字,当此phase中最后一个party也arrive以后,phase数字将会增加,即phase进入下一个周期,同时触发(onAdvance)那些阻塞在上一phase的线程。这一点类似于CyclicBarrier的barrier到达机制;更灵活的是,我们可以通过重写onAdvance方法来实现更多的触发行为。
 
    2.2)Waiting:Phaser中的awaitAdvance()方法,需要指定一个phase数字,表示此Thread阻塞直到phase推进到此周期,arriveAndAwaitAdvance()方法阻塞到下一周期开始(或者当前phase结束)。不像CyclicBarrier,即使等待Thread已经interrupted,awaitAdvance方法会继续等待。Phaser提供了Interruptible和Timout的阻塞机制,不过当线程Interrupted或者timout之后将会抛出异常,而不会修改Phaser的内部状态。如果必要的话,你可以在遇到此类异常时,进行相应的恢复操作,通常是在调用forceTermination()方法之后。
    Phaser通常在ForJoinPool中执行tasks,它可以在有task阻塞等待advance时,确保其他tasks的充分并行能力。
 
3、中断(终止):

Phaser可以进入Termination状态,可以通过isTermination()方法判断;当Phaser被终止后,所有的同步方法将会立即返回(解除阻塞),不需要等到advance(即advance也会解除阻塞),且这些阻塞方法将会返回一个负值的phase值(awaitAdvance方法、arriveAndAwaitAdvance方法)。当然,向一个termination状态的Phaser注册party将不会有效;此时onAdvance()方法也将会返回true(默认实现),即所有的parties都会被deregister,即register个数为0。
 
4、Tiering(分层):

Phaser可以“分层”,以tree的方式构建Phaser来降低“竞争”。如果一个Phaser中有大量parties,这会导致严重的同步竞争,所以我们可以将它们分组并共享一个parent Phaser,这样可以提高吞吐能力;Phaser中注册和注销parties都会有Child 和parent Phaser自动管理。当Child Phaser中中注册的parties变为非0时(在构造函数Phaser(Phaser parent,int parties),或者register()方法),Child Phaser将会注册到其Parent上;当Child Phaser中的parties变为0时(比如由arrivedAndDegister()方法),那么此时Child Phaser也将从其parent中注销出去。
 
5、监控:

同步的方法只会被register操作调用,对于当前state的监控方法可以在任何时候调用,比如getRegisteredParties()获取已经注册的parties个数,getPhase()获取当前phase周期数等;因为这些方法并非同步,所以只能反映当时的瞬间状态

 
二、常用的Barrier比较

1、CountDownLatch

//初始化parties线程个数  
int parties = 12;  
CountDownLatch latch = new CountDownLatch(parties);  
ExecutorService executor = Executors.newFixedThreadPool(parties);  

for(int i = 0; i < parties; i++) {      
    executor.execute(new Runnable() {                 
        public void run() {              
            try {                   
                Thread.sleep(3000);              
            } catch (Exception e) {}              
            finally {                  
                //当前任务执行完,倒计数器减1,直至所有任务都完成,倒计数器为0,唤醒主线程
                latch.countDown();              
            }          
        }      
    });  
}  

latch.await();//主线程阻塞,直到所有的parties到达
    
executor.shutdown();

2、CyclicBarrier

//初始化parties线程个数  
int parties = 12;  
CyclicBarrier barrier = new CyclicBarrier(parties);  
ExecutorService executor = Executors.newFixedThreadPool(parties);  

for(int i = 0; i < parties; i++) {      
    executor.execute(new Runnable() {          
        public void run() {              
            try {                  
                int i = 0;                  
                while (i < 3 && !barrier.isBroken()) {                      
                    System.out.println("tid:"+Thread.currentThread().getId());                      
                    Thread.sleep(3000);                      
                    //如果所有的parties都到达,则开启新的一次周期(generation),barrier可以被重用                   
                    barrier.await();                      
                    i++;                  
                }                
            } 
            catch (Exception e) {}              
            finally {}          
        }      
    });  
}
  
Thread.sleep(100000);  

 

三、API

     1、Phaser():构造函数,创建一个Phaser;默认parties个数为0。此后我们可以通过register()、bulkRegister()方法来注册新的parties。每个Phaser实例内部,都持有几个状态数据:termination状态、已经注册的parties个数(registeredParties)、当前phase下已到达的parties个数(arrivedParties)、当前phase周期数,还有2个同步阻塞队列Queue。Queue中保存了所有的waiter,即因为advance而等待的线程信息;这两个Queue分别为evenQ和oddQ,这两个Queue在实现上没有任何区别,Queue的元素为QNode,每个QNode保存一个waiter的信息,比如Thread引用、阻塞的phase、超时的deadline、是否支持interrupted响应等。两个Queue,其中一个保存当前phase中正在使用的waiter,另一个备用,当phase为奇数时使用evenQ、oddQ备用,偶数时相反,即两个Queue轮换使用。当advance事件触发期间,新register的parties将会被放在备用的Queue中,advance只需要响应另一个Queue中的waiters即可,避免出现混乱。
 
    2、Phaser(int parties):构造函数,初始一定数量的parties;相当于直接regsiter此数量的parties。
    3、arrive():到达,阻塞,等到当前phase下其他parties到达。如果没有register(即已register数量为0),调用此方法将会抛出异常,此方法返回当前phase周期数,如果Phaser已经终止,则返回负数。
    4、arriveAndDeregister():到达,并注销一个parties数量,非阻塞方法。注销,将会导致Phaser内部的parties个数减一(只影响当前phase),即下一个phase需要等待arrive的parties数量将减一。异常机制和返回值,与arrive方法一致。
    5、arriveAndAwaitAdvance():到达,且阻塞直到其他parties都到达,且advance。此方法等同于awaitAdvance(arrive())。如果你希望阻塞机制支持timeout、interrupted响应,可以使用类似的其他方法(参见下文)。如果你希望到达后且注销,而且阻塞等到当前phase下其他的parties到达,可以使用awaitAdvance(arriveAndDeregister())方法组合。此方法的异常机制和返回值同arrive()。
    6、awaitAdvance(int phase):阻塞方法,等待phase周期数下其他所有的parties都到达。如果指定的phase与Phaser当前的phase不一致,则立即返回。
    7、awaitAdvanceInterruptibly(int phase):阻塞方法,同awaitAdvance,只是支持interrupted响应,即waiter线程如果被外部中断,则此方法立即返回,并抛出InterrutedException。
    8、awaitAdvanceInterruptibly(int phase,long timeout,TimeUnit unit):阻塞方法,同awaitAdvance,支持timeout类型的interrupted响应,即当前线程阻塞等待约定的时长,超时后以TimeoutException异常方式返回。
    9、forceTermination():强制终止,此后Phaser对象将不可用,即register等将不再有效。此方法将会导致Queue中所有的waiter线程被唤醒。
    10、register():新注册一个party,导致Phaser内部registerPaties数量加1;如果此时onAdvance方法正在执行,此方法将会等待它执行完毕后才会返回。此方法返回当前的phase周期数,如果Phaser已经中断,将会返回负数。
    11、bulkRegister(int parties):批量注册多个parties数组,规则同10、。
    12、getArrivedParties():获取已经到达的parties个数。
    13、getPhase():获取当前phase周期数。如果Phaser已经中断,则返回负值。
    14、getRegisteredParties():获取已经注册的parties个数。
    15、getUnarrivedParties():获取尚未到达的parties个数。
    16、onAdvance(int phase,int registeredParties):这个方法比较特殊,表示当进入下一个phase时可以进行的事件处理,如果返回true表示此Phaser应该终止(此后将会把Phaser的状态为termination,即isTermination()将返回true。),否则可以继续进行。phase参数表示当前周期数,registeredParties表示当前已经注册的parties个数。
    默认实现为:return registeredParties == 0;在很多情况下,开发者可以通过重写此方法,来实现自定义的advance时间处理机制。
 
    内部原理,比较简单(简述):
    1)两个计数器:分别表示parties个数和当前phase。register和deregister会触发parties变更(CAS),全部parties到达(arrive)会触发phase变更。
    2)一个主要的阻塞队列:非AQS实现,对于arriveAndWait的线程,会被添加到队列中并被park阻塞,知道当前phase中最后一个party到达后触发唤醒。

猜你喜欢

转载自blog.csdn.net/mmlz00/article/details/83790489