知识点干货—多线程同步【4】之Phaser

本系列之前的标题是“面试知识点”,由于一些知识点并不都是面试时才用到,却是日常开发经常出现的,比如CyclicBarriar和Phaser,所以后续会将这个系列的标题改为“知识点干货”,希望能给大家带来更多优质的学习干货。

继续总结多线程同步常用的方法或者类,之前介绍了CountDownLatch,CyclicBarriar和Exchanger,这次介绍一个能同时替代CountDownLatch和CyclicBarriar的新类。

Phaser–移相器

Phaser的一个特别典型的应用场景是:它可以替代使用CountDownLatch和CyclicBarriar的任何场景。例如,在进行高并发多线程测试时,有时需要控制线程的启动时机,让它们能够同时启动,从而获得最大程度的并发,通常我们会使用CountDownLatch,这个完全可以使用Phaser进行替代。再如,我们需要某些任务在某一个阶段同时执行,再所有任务执行完成后, 再同时进行到下一个阶段继续执行,我们首先想到的是CyclicBarriar,而使用Phaser也可以完全代替CyclicBarriar。

1、Phaser的定义

Phaser是jdk1.7上新增的一个类,它是一个灵活的多线程同步工具,包含了CyclicBarrier和CountDownLatch的大部分功能,同时也具有一些它们不支持的特性。

在使用它之前,需要了解两个概念,分别是phase和party。
phase就是阶段的意思,初值为0。当所有的线程执行任务时,它是分阶段的,每个阶段对应一个phase,当所有线程执行完本轮任务后,表示本阶段结束,进入到下一阶段,phase的值自动加1。
而party表示线程的个数,party=3表示Phaser对象当前管理着3个线程。通过方法可以
动态增parties计数,而这一点CyclicBarrier类是做不到的,它的线程数是在创建时设置好的。

2、常用方法

Phaser对外提供的方法很多,我们只介绍几个重要的。

● Phaser()

无参构造函数,创建一个Phaser对象。默认parties个数为0。后续可以通过register()、bulkRegister()方法来修改新的parties。在每个Phaser实例内部,会持有几个状态数据:终止状态、已经注册的parties个数、当前phase下已到达的parties个数、当前phase阶段数。

● Phaser(int parties)

有参构造函数,一个Phaser对象,并初始一定数量的parties,相当于在初始化Phaser实例后,再regsiter此数量的parties。

● arrive()

一直阻塞,等待当前phase下其他parties到达。parties必须大于0,如果使用如果Phaser()创建实例,parties没有被register具体的值,调用此方法将会抛出异常。此方法同时返回当前phase周期数,如果Phaser实例已经终止,则返回负数。

● awaitAdvance(int phase)

阻塞方法,等待phase周期数下其他所有的parties都到达,参数指定了当前阻塞的phase周期阶段数。如果指定的phase与Phaser实例当前的phase不一致,会立即返回。可以这样来使用awaitAdvance(arrive())。

● arriveAndAwaitAdvance()

到达某个状态并且阻塞直到其他parties都到达,且advance。此方法等同于调用awaitAdvance(arrive())。

● awaitAdvanceInterruptibly(int phase)

阻塞方法,同awaitAdvance,并且支持interrupted响应,参数指明是那个阶段。waiter线程可以被外部中断,在被中断时此方法立即返回,并抛出InterrutedException异常。

● awaitAdvanceInterruptibly(int phase,long timeout,TimeUnit unit)

阻塞方法,同awaitAdvance,支持timeout类型的interrupted响应,参数指明是那个阶段。当当前线程阻塞等待timeout时长后,抛出TimeoutException异常。
如果你希望阻塞机制支持timeout、interrupted响应,可以使用类似的awaitAdvanceInterruptibly两种重载的方法。

● onAdvance(int phase,int registeredParties)

这个方法很重要,是一个回调方法。当进入下一个phase时会回调到这个方法中进行处理。如果它返回true表示此Phaser应该终止(Phaser的状态设为termination),否则可以继续进行。phase参数表示当前周期数,registeredParties表示当前已经注册的parties个数。
大多情况下,开发者应该重写此方法,来实现自定义的advance回调处理机制。

● register()

注册一个party,每调用一次就会使Phaser实例内部Paties数量加1。如果此时onAdvance方法正在执行,此方法将会等待它执行完毕后才会返回。此方法返回当前的phase周期数,如果Phaser已经中断,将会返回负数。

● bulkRegister(int parties)

进行批量注册多个parties,其它功能和register()相同。

● getArrivedParties()

获取到达的parties个数。

● getPhase()

获取当前phase周期数,如果Phaser已经中断,则返回负值。

● getRegisteredParties()

获取注册的parties个数。

● getUnarrivedParties()

获取还未到达的parties个数。

3、演示代码

Phaser的使用场景很多,我们这里只举一个例子。

//创建时,指定参与的parties个数
int parties = 3;
//也可以在创建时不指定parties,通过使用register或者bulkRegister随时注册
Phaser phaser = new Phaser();
//主线程先注册一个party,因为register()执行一次只能创建一个party
//类似与CountDownLatch,在主线程中等待所有的parties到达后再解除阻塞
phaser.register();
ExecutorService executor = Executors.newFixedThreadPool(parties);
for(int i = 0; i < parties; i++) {
  phaser.register();//每创建一个线程,我们就注册一个party
  final int threadId = i;
  executor.execute(new Runnable() {
     @Override
     public void run() {
        try {
           int i = 0;
           //2的意思是最大周期
           while (i < 2 && !phaser.isTerminated()) {
              System.out.println("current threadId :"+threadId+",phase:" + phaser.getPhase());
              //模拟每个线程进行的工作
              Thread.sleep(1000);
              //等待同一周期内其它线程到来,然后进入新的周期,从而同步进行
              phaser.arriveAndAwaitAdvance();
              i++;
           }
        } catch (Exception e) {
           e.printStackTrace();
        }

        finally {
           phaser.arriveAndDeregister();
        }
     }
  });
}
//主线程到达,且注销自己,线程池中的线程可开始分周期进行执行
System.out.println("main thread arriveAndDeregister!");
phaser.arriveAndDeregister();

执行结果应该是如下这样的格式,

main thread arriveAndDeregister!
current threadId :0,phase:0
current threadId :1,phase:0
current threadId :2,phase:0
current threadId :2,phase:1
current threadId :0,phase:1
current threadId :1,phase:1

4、总结

综上,我们可以使用Phaser替代CountDownLatch。对于CountDownLatch而言,
它的两个重要的方法,await()方法和countDown(),在Phaser中,可以用awaitAdvance(int n)和arrive()分别来代替,前者会使线程进入等待状态,后者使计数器减一,当计数器为0时所有等待的线程开始执行。
用Phaser替代CyclicBarrier更简单,CyclicBarrier的await()方法可以直接用Phaser的arriveAndAwaitAdvance()方法替代。
所以,在使用CountDownLatch或者CyclicBarrier时,我们可以考虑使用Phaser来代替,效率会更高一些。

这里写图片描述

本公众号将以推送Android各种技术干货或碎片化知识,以及整理老司机日常工作中踩过的坑涉及到的经验知识为主,也会不定期将正在学习使用的新技术总结出来进行分享。每天一点干货小知识把你的碎片时间充分利用起来。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值