文档章节

Java并发编程中级篇(五):更强大的多阶段并发控制Phaser

阿拉德大陆的魔法师
 阿拉德大陆的魔法师
发布于 2016/11/26 16:55
字数 1379
阅读 121
收藏 3

Java API还提供了一个强大的同步辅助类Pahser,它可以控制多阶段并发辅助任务。当我们有并发任务,并且需要分阶段执行,每阶段都需要等待所有线程执行本阶段执行完毕才能够继续执行,这种机制就非常好用。Phaser类同样需要一个整形作为初始化参数来确定有几个线程参与执行。

下面我们来看一个例子,在这个例子中我们把一个并发任务分为三个阶段,每一阶段都需要所有线程完成后才能继续执行下一阶段的任务。

我们来定义一个带有Phaser机制的线程类PhaserRunnable,在线程开始运行的时候调用arriverAndAwaitAdvance()方法来代表线程已经进入执行状态,其实这个也可以算作一步,就是等待所有任务线程都启动后大家一起执行。然后开始执行第一步任务,每个线程随机休眠一段时间来模拟任务执行耗时,执行完毕后调用arriverAndAwaitAdvance()来表示任务执行完毕,然后等待其他线程,等所有线程都滴啊用arriverAndAwaitAdvance()方法后,所有休眠的线程都被唤醒然后继续执行第二步。也是随机休眠一段时间后,第二部执行完毕,但是这里有一个不同,如果休眠时间是一个双数那么线程将不再执行第三步操作而是直接返回,这里要调用phaser.arriveAndDeregister()方法来表示线程已经结束,以后不再需要等待我了。

public class PhaserRunnable implements Runnable{
    private Phaser phaser;

    public PhaserRunnable(Phaser phaser) {
        this.phaser = phaser;
    }

    @Override
    public void run() {
        phaser.arriveAndAwaitAdvance();
        System.out.printf("%s: start.\n", Thread.currentThread().getName());

        long duration1 = (long) (Math.random() * 10) + 1;
        System.out.printf("%s: step1 start duration %d seconds.\n", Thread.currentThread().getName(), duration1);
        try {
            TimeUnit.SECONDS.sleep(duration1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.printf("%s: step1 done.\n", Thread.currentThread().getName());
        phaser.arriveAndAwaitAdvance();

        long duration2 = (long) (Math.random() * 10) + 1;
        System.out.printf("%s: step2 start duration %d seconds.\n", Thread.currentThread().getName(), duration2);
        try {
            TimeUnit.SECONDS.sleep(duration2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (duration2 % 2 == 0) {
            System.out.printf("%s: step2 done and task finished.\n", Thread.currentThread().getName());
            phaser.arriveAndDeregister();
            return;
        } else {
            System.out.printf("%s: step2 done.\n", Thread.currentThread().getName());
            phaser.arriveAndAwaitAdvance();
        }

        long duration3 = (long) (Math.random() * 10) + 1;
        System.out.printf("%s: step3 start duration %d seconds.\n", Thread.currentThread().getName(), duration3);
        try {
            TimeUnit.SECONDS.sleep(duration3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.printf("%s: step3 done.\n", Thread.currentThread().getName());
        phaser.arriveAndDeregister();
    }
}

主方法类中启动三个任务线程,如果第二步都是休眠单数秒你可以尝试多运行几次,最后打印Phaser状态。

public class Main {
    public static void main(String[] args) {
        Phaser phaser = new Phaser(3);

        Thread[] threads = new Thread[3];

        for (int i = 0; i < 3; i++) {
            threads[i] = new Thread(new PhaserRunnable(phaser));
            threads[i].start();
        }

        for (int i = 0; i < 3; i++) {
            try {
                threads[i].join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.printf("%s: Phaser terminated.\n", Thread.currentThread().getName());
    }
}

查看控制台日志: 

Thread-2: start.
Thread-1: start.
Thread-0: start.
Thread-2: step1 start duration 9 seconds.
Thread-0: step1 start duration 2 seconds.
Thread-1: step1 start duration 6 seconds.
Thread-0: step1 done.
Thread-1: step1 done.
Thread-2: step1 done.
Thread-2: step2 start duration 7 seconds.
Thread-0: step2 start duration 2 seconds.
Thread-1: step2 start duration 1 seconds.
Thread-1: step2 done.
Thread-0: step2 done and task finished.
Thread-2: step2 done.
Thread-2: step3 start duration 3 seconds.
Thread-1: step3 start duration 7 seconds.
Thread-2: step3 done.
Thread-1: step3 done.
main: Phaser terminated.

Phaser类有两种状态:Active和Termination。有任务参与的时候Phaser状态为Active;当所有参与同步的线程都结束后Phaser也就没有参与者了,这时Phaser进入了Termination态。当Phaser处于终止态,同步方法arriveAndAwaitAdvance()会立即返回。

Phaser类的一个重大特性就是不必对它的方法进行异常处理。不像其他通不辅助类,被Phaser类置于休眠状态的线程不会响应中断事件,也不会抛出InterruptedException异常。

Phaser类还提供了一些改变Phaser对象的方法,这些方法如下。

  • arriver():这个方法通知phaser对象一个参与者已经完成了当前阶段,但是他等待其他参与者都完成当前阶段。必须小心使用这个方法,因为他不会与其它线程同步。
  • awaitAdvance(int phase):如果传入的阶段参数与当前阶段一致,那么这个方法会将当前线程置于休眠,直到这个阶段的所有参与者都完成运行。如果传入阶段参数与当前阶段不一致,这个方法会立即返回。
  • awaitAdvanceInterruptibly(int phaser):这个方法跟awaitAdvance(int phase)一样,不同之处在于,如果这个方法中休眠的线程被中断,它将抛出InterruptedException异常。

Phaser类可以动态地改变参与线程的数量:

  • register():这个方法可以将一个新的线程注册到Phaser中,这个新的参与者被当成本阶段的任务来执行。
  • bulkRegister(int Parties):这个方法将指定数目的参与者注册到Phaser中,所有这些新的参与者都将被当成本阶段的任务来执行。

Phaser提供了一个方法forceTermination()方法来强制Phaser进入Termination状态,这个方法不管Phaser中是否还有注册的线程。当一个参与的线程出现错误,强制phaser终止是有意义的。当一个Phaser处于Termination状态的时候,awaitAdvance()和arriveAndAwaitAdvance()方法都立刻返回一个负数,这样就可以验证Phaser状态是不是终止了,可以根据这个状态来终止线程的执行,直接返回或者做一些处理,比如数据回滚什么的。

© 著作权归作者所有

阿拉德大陆的魔法师
粉丝 27
博文 91
码字总数 83019
作品 0
西城
程序员
私信 提问
java多线程系列:通过对战游戏学习CyclicBarrier

CyclicBarrier是java.util.concurrent包下面的一个工具类,字面意思是可循环使用(Cyclic)的屏障(Barrier),通过它可以实现让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一...

huangzd
2018/01/06
0
0
死磕 java同步系列之Phaser源码解析

问题 (1)Phaser是什么? (2)Phaser具有哪些特性? (3)Phaser相对于CyclicBarrier和CountDownLatch的优势? 简介 Phaser,翻译为阶段,它适用于这样一种场景,一个大任务可以分为多个阶...

彤哥读源码
10/01
30
0
干货系列1:Java互联网网站开发工程师 的技术提高与晋升路线(技术专精)

前几天写了自己对于Java软件开发工程师职业发展规划方面的一些感悟,陆续收到一些反馈,希望我能再就Java工程师不同的开发(职责)方向谈谈职业发展问题。(上一篇:Java软件开发工程师的自我...

半饱即好
2018/06/26
0
0
读书笔记之《Java并发编程的艺术》-并发编程容器和框架(重要)

读书笔记部分内容来源书出版书,版权归本书作者,如有错误,请指正。 欢迎star、fork,读书笔记系列会同步更新 git https://github.com/xuminwlt/j360-jdk module j360-jdk-thread/me.j360....

Hi徐敏
2015/11/11
723
1
那些年,关于 Java 的那些事儿

版权声明:Follow your heart and intuition. https://blog.csdn.net/qq_35246620/article/details/78695893 温馨提示:本系列博文(含示例代码)已经同步到 GitHub,地址为「java-skills」,...

维C果糖
2017/12/02
0
0

没有更多内容

加载失败,请刷新页面

加载更多

mysql-connector-java升级到8.0后保存时间到数据库出现了时差

在一个新项目中用到了新版的mysql jdbc 驱动 <dependency>     <groupId>mysql</groupId>     <artifactId>mysql-connector-java</artifactId>     <version>8.0.18</version> ......

ValSong
今天
5
0
Spring Boot 如何部署到 Linux 中的服务

打包完成后的 Spring Boot 程序如何部署到 Linux 上的服务? 你可以参考官方的有关部署 Spring Boot 为 Linux 服务的文档。 文档链接如下: https://docs.ossez.com/spring-boot-docs/docs/r...

honeymoose
今天
6
0
Spring Boot 2 实战:使用 Spring Boot Admin 监控你的应用

1. 前言 生产上对 Web 应用 的监控是十分必要的。我们可以近乎实时来对应用的健康、性能等其他指标进行监控来及时应对一些突发情况。避免一些故障的发生。对于 Spring Boot 应用来说我们可以...

码农小胖哥
今天
9
0
ZetCode 教程翻译计划正式启动 | ApacheCN

原文:ZetCode 协议:CC BY-NC-SA 4.0 欢迎任何人参与和完善:一个人可以走的很快,但是一群人却可以走的更远。 ApacheCN 学习资源 贡献指南 本项目需要校对,欢迎大家提交 Pull Request。 ...

ApacheCN_飞龙
今天
5
0
CSS定位

CSS定位 relative相对定位 absolute绝对定位 fixed和sticky及zIndex relative相对定位 position特性:css position属性用于指定一个元素在文档中的定位方式。top、right、bottom、left属性则...

studywin
今天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部