前面的文章中我们讲到了CyclicBarrier、CountDownLatch的使用,这里再回顾一下CountDownLatch主要用在一个线程等待多个线程执行完毕的情况,而CyclicBarrier用在多个线程互相等待执行完毕的情况。

Phaser是java 7 引入的新的并发API。他引入了新的Phaser的概念,我们可以将其看成一个一个的阶段,每个阶段都有需要执行的线程任务,任务执行完毕就进入下一个阶段。所以Phaser特别适合使用在重复执行或者重用的情况。

基本使用

在CyclicBarrier、CountDownLatch中,我们使用计数器来控制程序的顺序执行,同样的在Phaser中也是通过计数器来控制。在Phaser中计数器叫做parties, 我们可以通过Phaser的构造函数或者register()方法来注册。

通过调用register()方法,我们可以动态的控制phaser的个数。如果我们需要取消注册,则可以调用arriveAndDeregister()方法。

我们看下arrive:

    public int arrive() {
return doArrive(ONE_ARRIVAL);
}

Phaser中arrive实际上调用了doArrive方法,doArrive接收一个adjust参数,ONE_ARRIVAL表示arrive,ONE_DEREGISTER表示arriveAndDeregister。

Phaser中的arrive()、arriveAndDeregister()方法,这两个方法不会阻塞,但是会返回相应的phase数字,当此phase中最后一个party也arrive以后,phase数字将会增加,即phase进入下一个周期,同时触发(onAdvance)那些阻塞在上一phase的线程。这一点类似于CyclicBarrier的barrier到达机制;更灵活的是,我们可以通过重写onAdvance方法来实现更多的触发行为。

下面看一个基本的使用:

    void runTasks(List<Runnable> tasks) {
final Phaser phaser = new Phaser(1); // "1" to register self
// create and start threads
for (final Runnable task : tasks) {
phaser.register();
new Thread() {
public void run() {
phaser.arriveAndAwaitAdvance(); // await all creation
task.run();
}
}.start();
} // allow threads to start and deregister self
phaser.arriveAndDeregister();
}

上面的例子中,我们在执行每个Runnable之前调用register()来注册, 然后调用arriveAndAwaitAdvance()来等待这一个Phaser周期结束。最后我们调用 phaser.arriveAndDeregister();来取消注册主线程。

下面来详细的分析一下运行步骤:

  1. final Phaser phaser = new Phaser(1);

这一步我们初始化了一个Phaser,并且指定其现在party的个数为1。

  1. phaser.register();

这一步注册Runnable task到phaser,同时将party+1。

  1. phaser.arriveAndAwaitAdvance()

这一步将会等待直到所有的party都arrive。这里只会将步骤2中注册的party标记为arrive,而步骤1中初始化的party一直都没有被arrive。

  1. phaser.arriveAndDeregister();

在主线程中,arrive了步骤1中的party,并且将party的个数减一。

  1. 步骤3中的phaser.arriveAndAwaitAdvance()将会继续执行,因为最后一个phaser在步骤4中arrive了。

多个Phaser周期

Phaser的值是从0到Integer.MAX_VALUE,每个周期过后该值就会加一,如果到达Integer.MAX_VALUE则会继续从0开始。

如果我们执行多个Phaser周期,则可以重写onAdvance方法:

    protected boolean onAdvance(int phase, int registeredParties) {
return registeredParties == 0;
}

onAdvance将会在最后一个arrive()调用的时候被调用,如果这个时候registeredParties为0的话,该Phaser将会调用isTerminated方法结束该Phaser。

如果要实现多周期的情况,我们可以重写这个方法:

protected boolean onAdvance(int phase, int registeredParties) {
return phase >= iterations || registeredParties == 0;
}

上面的例子中,如果phase次数超过了指定的iterations次数则就会自动终止。

我们看下实际的例子:

   void startTasks(List<Runnable> tasks, final int iterations) {
final Phaser phaser = new Phaser() {
protected boolean onAdvance(int phase, int registeredParties) {
return phase >= iterations || registeredParties == 0;
}
};
phaser.register();
for (final Runnable task : tasks) {
phaser.register();
new Thread() {
public void run() {
do {
task.run();
phaser.arriveAndAwaitAdvance();
} while (!phaser.isTerminated());
}
}.start();
}
phaser.arriveAndDeregister(); // deregister self, don't wait
}

上面的例子将会执行iterations次。

本文的例子请参考https://github.com/ddean2009/learn-java-concurrency/tree/master/Phaser

更多内容请访问flydean的博客

Phaser都不懂,还学什么多线程的更多相关文章

  1. 都2019年了,Java为什么还在坚持多线程不选择协程?

    都2019年了,Java为什么还在坚持多线程不选择协程? - 知乎 https://www.zhihu.com/question/332042250/answer/734051666

  2. 双向链表都不懂,还说懂Redis?

    目录 redis源码分析系列文章 前言 API使用 lpush左侧插入数据 rpush右侧插入数据 删除某个数据 修改某个数据 具体逻辑图 双向链表的定义 节点ListNode 整体架构 双向链表的实 ...

  3. 萌新看过来,你还学不懂VScode插件吗?

    一.前言 VSCode是微软家一个非常轻量化的编辑器,体量虽轻,但是却有异常强大的功能.原因在于VSCode许多强大功能都是基于插件实现的,IDE只提供一个最基本的框架和基本功能,我们需要使用插件来丰 ...

  4. [干货] 有了微信小程序,谁还学ReactNative?

    版权声明:本文由贺嘉原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/145 来源:腾云阁 https://www.qclou ...

  5. 跟我学Java多线程——线程池与堵塞队列

    前言 上一篇文章中我们将ThreadPoolExecutor进行了深入的学习和介绍,实际上我们在项目中应用的时候非常少有直接应用ThreadPoolExecutor来创建线程池的.在jdk的api中有 ...

  6. webpack从什么都不懂到入门

    前言 这篇文章是自己在整理webpack相关的东西时候突发奇想,想总结自己所学知识,也希望能够帮助想学习webpack的同学们,都是入门级别的,大佬请出门右转. 本文的webpack基于webpack ...

  7. C#9.0 终于来了,您还学的动吗? 带上VS一起解读吧!(应该是全网第一篇)

    一:背景 1. 讲故事 好消息,.NET 5.0 终于在2020年6月10日发布了第五个预览版,眼尖的同学一定看到了在这个版本中终于支持了 C# 9.0,此处有掌声,太好了!!! .Net5官方链接 ...

  8. 人人都能学会的 Python 多线程指南~

    大家好鸭!有没有想我~(https://jq.qq.com/?_wv=1027&k=rX9CWKg4) 在 Python 中,多线程最常见的一个场景就是爬虫,例如这样一个需求,有多个结构一样的 ...

  9. 月薪3万+的大数据人都在疯学Flink,为什么?

    身处大数据圈近5年了,在我的概念里一直认为大数据最牛的两个东西是Hadoop和Spark.18年下半年的时候,我突然发现身边很多大数据牛人都是研究学习Flink,甚至连Spark都大有被冷落抛弃的感觉 ...

随机推荐

  1. Light of future-冲刺Day 7

    目录 1.SCRUM部分: 每个成员进度 SCRUM 会议的照片 签入记录 代码运行截图 用户浏览界面 订单详情界面 管理员浏览界面 新增后台界面 2.PM 报告: 时间表 燃尽图 任务总量变化曲线 ...

  2. 我去,还在这样读写 excel 这也太低效了吧!

    前言 博文地址:https://sourl.cn/SsD3AM 最近读者小 H 给小黑哥发来私信: 小黑哥,最近我在负责公司报表平台开发,需要导出报表到 excel 中.每次使用 POI 开发,都要写 ...

  3. 电商平台--Mysql主从搭建(2)

    Master上授权从库: ```grant replication slave on *.* to slave1@ip identified by 'password';``` 基于数据库hotcop ...

  4. uni-app在线引入阿里字体图标库

    第一步 在app.vue中引入阿里字体图标库 第二步 在任意页面使用就可以了 <view class="item" v-for="(value,index) in ...

  5. django实现自定义登陆验证

    django实现自定义登陆验证 自定义装饰器函数和类 from utils.http import HttpResponseUnauthorized from django.views import ...

  6. Spring XML Bean 定义的加载和注册

    前言 本篇文章主要介绍 Spring IoC 容器怎么加载 bean 的定义元信息. 下图是一个大致的流程图: 第一次画图,画的有点烂.

  7. 判断一组checkbox/redio是否被选中,为其添加样式

    业务场景:当一行中有一个CheckBox被选中,则为此行添加class. <script type="text/javascript"> $(function(){ $ ...

  8. Golang 性能测试 (3) 跟踪刨析 golang trace

    简介 对于绝大部分服务,跟踪刨析是用不到的.但是如果遇到了下面问题,可以不妨一试: 怀疑哪个协程慢了 系统调用有问题 协程调度问题 (chan 交互.互斥锁.信号量等) 怀疑是 gc (Garbage ...

  9. MyBatis-Plus使用小结

    官网: https://mybatis.plus/ https://gitee.com/baomidou/mybatis-plus https://github.com/baomidou/mybati ...

  10. JAVA+HttpServletRequest文件上传

    public Result fileUp(HttpServletRequest request) { RowsVo vo = new RowsVo(); MultipartHttpServletReq ...