前面的文章中我们讲到了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. win10下vue-devtools的安装和使用

    网上关于vue-devtools的安装数不胜数,但是自己操作起来却总是遇到问题. 写下这篇随笔,以防以后忘记. vue-devtools是一款基于chrome游览器的插件,用于调试vue应用,这可以极 ...

  2. twoway 系列命令绘制地图

    twoway 系列命令绘制地图 ❝ 在谈到用stata绘制地图时,很多人首先想到的是spmap命令.其实,最常见的twoway系列命令就可以完成我们的大多数绘图需求. 对于spmap命令的使用,在线资 ...

  3. python中的两个高阶函数map()和reduce()

    1.map()传入的有两个参数,函数和可迭代对象(Itreable),map()是把传入的函数依次作用于序列的每个元素,结果返回的是一个新的可迭代对象(Iterable). map()代码如下: # ...

  4. Mysql数据库错误代码大全

                                                    Mysql数据库错误代码大全 出现较多的一些网页代码提示的意思: 1016错误:文件无法打开,使用后台修 ...

  5. 使用rem配置PC端自适应大屏

    效果如下 使得大屏不论在什么宽高比例依然能展示全部数据 安装 npm install -S postcss-pxtorem rem配置思路 原先的rem函数是能解决大部分的问题的,如果展示不全,也可以 ...

  6. Java编程最差实践常见问题详细说明(2)转

    Java编程最差实践常见问题详细说明(2)转 2012-12-13 13:57:20|  分类: JAVA |  标签:java  |举报|字号 订阅     反射使用不当  错误的写法: Java代 ...

  7. 数据结构和算法(Golang实现)(12)常见数据结构-链表

    链表 讲数据结构就离不开讲链表.因为数据结构是用来组织数据的,如何将一个数据关联到另外一个数据呢?链表可以将数据和数据之间关联起来,从一个数据指向另外一个数据. 一.链表 定义: 链表由一个个数据节点 ...

  8. 001-iOS开发前奏-C语言笔记

    001-iOS开发前奏-C语言笔记 学习目标 1.[了解]操作系统 2.[了解]应用软件 3.[了解]操作系统的分类和市场占有份额 4.[了解]iOS操作系统 5.[了解]应用软件开发的分类 6.[了 ...

  9. Shell 变量引用实例

    初学 Shell 编程时,对变量各种引用使用不太熟悉,走了很多弯路,本文记录变量引用的一些用法,希望对大家有所帮助. 引用 引用指将字符串用引用符号引起来,以防止特殊字符被 shell 脚本解释为其他 ...

  10. ASE课程总结 by 张葳

    本期ASE课程分为两个阶段,第一阶段的personal project与第二阶段的team project,其中,第一阶段旨在锻炼我们个人的问题解决能力和编程能力,第二阶段则锻炼主要我们的管理能力,合 ...