Java7:理解Phaser

转自 http://hrps.me/2016/11/22/java-concurrent-phaser/

Java7引入一种称为Phaser的灵活的线程同步机制。如果你需要所有线程到达之后,才继续或者开始进行新的一组任务,Phaser是一个很好的选择。
下面是代码,及每一步的解释。
感觉原代码解释的不好,我从新写了个感觉更好理解。😁

import java.util.concurrent.Phaser;

/**
 * Description:
 * User: Huang rp
 * Date: 16/11/23
 * Version: 1.0
 */
public class PhaserSamples {

    public static void main(String[] args) throws InterruptedException {
        new PhaserSamples().runTasks();

    }

    // 一共开几场会
    private static final int PHASE_TO_TERMINATE = 2;
    // 初始参会人员数量
    private static final int INIT_PARTIES = 1;
    // 增加参会人员数量
    private static final int ADD_PARTIES = 5;
    // 每个会场限制参会者数量
    private static final int TASKS_PER_PHASER = 10;

    void runTasks() throws InterruptedException {
        final Phaser phaser = new Phaser(INIT_PARTIES) {
            // 所有人员到达完毕,开始会议;连续开PHASE_TO_TERMINATE - 1 场会议,会议结束(terminal)
            @Override
            protected boolean onAdvance(int phase, int registeredParties) {
                System.out.println("第" + (phase + 1) + "场会议结束");
                return phase == (PHASE_TO_TERMINATE - 1) || registeredParties == 0;
            }
        };
        final Task tasks[] = new Task[ADD_PARTIES];

        System.out.println("会议开始准备,需要至少" + INIT_PARTIES + "人签到, 共连续" + PHASE_TO_TERMINATE + "场会议,分会场数量" +
                (ADD_PARTIES % TASKS_PER_PHASER == 0 ? (ADD_PARTIES / TASKS_PER_PHASER) : (ADD_PARTIES / TASKS_PER_PHASER + 1)));
        System.out.println("Main准备参加第一场会议,开始签到,共" + phaser.getRegisteredParties() + "人签到");

        build(tasks, 0, tasks.length, phaser);
        for (int i = 0; i < tasks.length; i++) {
            final Thread thread = new Thread(tasks[i]);
            thread.start();
        }

        phaser.arriveAndDeregister();
        System.out.println("Main离开会场");
        // 准备下一场会议,如果没有再到达会场,所有人都将等待
//        phaser.arriveAndAwaitAdvance();
//        System.out.println("Main休息准备下一场会议");
    }

    public static void build(Task[] tasks, int lo, int hi, Phaser ph) {
        if (hi - lo > TASKS_PER_PHASER) {
            for (int i = lo; i < hi; i += TASKS_PER_PHASER) {
                int j = Math.min(i + TASKS_PER_PHASER, hi);
                build(tasks, i, j, new Phaser(ph));
            }
        } else {
            for (int i = lo; i < hi; ++i)
                tasks[i] = new Task(i + 1, ph);
        }
    }


    public static class Task implements Runnable {
        private final int id;
        private final Phaser phaser;

        public Task(int id, Phaser phaser) {
            this.id = id;
            this.phaser = phaser;
            this.phaser.register();
            System.out.println("参会人员" + id + "已经签到,共" + phaser.getRegisteredParties() + "人签到");
        }

        @Override
        public void run() {
            // 参加每一场会议
            while (!phaser.isTerminated()) {
                try {
                    // 签到之后,走到会场
                    Thread.sleep(20 * id + 20);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("参会人员" + this.id + "已经到达第" + (phaser.getPhase() + 1) + "场会议, 当前共有"
                        + phaser.getRegisteredParties() + " 人参会,"
                        + (phaser.getArrivedParties() + 1) + " 到达,"
                        + (phaser.getUnarrivedParties() - 1) + " 未到达");
                // 到达会场
                phaser.arriveAndAwaitAdvance();
            }
        }
    }
}

运行结果

会议开始准备,需要至少1人签到, 共连续2场会议,分会场数量1
Main准备参加第一场会议,开始签到,共1人签到
参会人员1已经签到,共2人签到
参会人员2已经签到,共3人签到
参会人员3已经签到,共4人签到
参会人员4已经签到,共5人签到
参会人员5已经签到,共6人签到
Main离开会场
参会人员1已经到达第1场会议, 当前共有5 人参会,1 到达,4 未到达
参会人员2已经到达第1场会议, 当前共有5 人参会,2 到达,3 未到达
参会人员3已经到达第1场会议, 当前共有5 人参会,3 到达,2 未到达
参会人员4已经到达第1场会议, 当前共有5 人参会,4 到达,1 未到达
参会人员5已经到达第1场会议, 当前共有5 人参会,5 到达,0 未到达
第1场会议结束
参会人员1已经到达第2场会议, 当前共有5 人参会,1 到达,4 未到达
参会人员2已经到达第2场会议, 当前共有5 人参会,2 到达,3 未到达
参会人员3已经到达第2场会议, 当前共有5 人参会,3 到达,2 未到达
参会人员4已经到达第2场会议, 当前共有5 人参会,4 到达,1 未到达
参会人员5已经到达第2场会议, 当前共有5 人参会,5 到达,0 未到达
第2场会议结束

有几点注意:
当签到的人员数量等于到达会场人员数量,将立即开会,所以第一个人员到达会场会比最后一个签到的晚才能保证所有人能参加会议。
可以一个会场容纳所有人开会,将同一个phaser传递给所有的task;也可以维护一个层级关系,建立多个会场进行同一个会议,将所有参会人员平均分配到各个会场。
签到之后,可以不用等待所有人到达会场可以立即离场,并且从签到簿上抹掉签名。
下一场会议开始之前,如果还有人员等待下一场会议,那么所有签到的人都必须参加下一场。如果所有人都不等待,会议结束。
所有人员参加完指定数量的会议后,会议结束。

Phaser终止的两种途径,Phaser维护的线程执行完毕或者onAdvance()返回true
此外Phaser还能维护一个树状的层级关系,构造的时候new Phaser(parentPhaser),对于Task执行时间短的场景(竞争激烈),** TASKS_PER_PHASER**值设置较小,反之适当增大。

名词解释:

  • party 对应一个线程,数量可以通过register或者初始化new Phaser(num)的时候控制
  • arrive 对应一个party的状态,初始时是unarrived,当调用arriveAndAwaitAdvance()或者 arriveAndDeregister()进入arrive状态,可以通过getUnarrivedParties()获取当前未到达的数量
  • register 注册一个party,每一阶段必须所有注册的party都到达才能进入下一阶段
  • phase 阶段,当所有注册的party都arrive之后,将会调用PhaseronAdvance()方法来判断是否要进入下一阶段
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容