Java 多线程之 Phaser(多阶段任务/同步辅助类)

一、概述

  • Phaser 也是Java并发编程中的一种同步辅助工具,用于线程之间的协调和同步。它提供了比CyclicBarrierCountDownLatch更灵活和强大的功能,可以用于更复杂的多线程协作场景。

  • Phaser的主要用途是将多个线程分为多个阶段,并在每个阶段进行同步。每个线程可以独立运行,但在特定的阶段需要等待其他线程到达屏障点。Phaser可以动态地适应线程的注册和注销,可以处理可变数量的参与者。

  • Phaser 常见用途:

    • 多阶段任务:Phaser 可以用于将一个任务分解为多个阶段,每个阶段由多个线程并行执行,等待所有线程完成当前阶段后再进入下一个阶段。
    • 多阶段并行算法:Phaser 可以用于实现多阶段的并行算法,每个阶段的计算可以由多个线程同时进行,等待所有线程完成当前阶段后再进入下一阶段。
    • 多线程协作:Phaser 可以用于多个线程之间的协作,实现一种分治的并发编程模式。
  • Phaser 的主要方法包括:

    • arriveAndAwaitAdvance():使当前线程到达同步点并等待其他线程,直到所有线程都到达同步点后才继续执行。

    • arriveAndDeregister():使当前线程到达同步点并注销,不再参与后续的同步操作。

    • getPhase():获取当前阶段的编号。

    • register():注册一个线程,使其参与后续的同步操作。

二、使用方法

  • 使用Phaser的基本步骤如下:

    • 创建Phaser对象,指定注册线程和阶段通知回调函数。
    • 注册线程,可以通过调用 phaser.register() 方法将线程注册到Phaser中。也可以在实始化对象时指定。
    • 定义阶段,在业务逻辑中规定好任务需要同步的阶段。
    • 执行任务:每个线程执行自己的任务,在需要同步的阶段调用 phaser.arriveAndAwaitAdvance() 方法等待其他线程到达屏障点。
    • 解除注册:当某个线程完成任务后,可以调 phaser.arriveAndDeregister() 方法解除线程的注册。
    import java.util.concurrent.Phaser;
    
    public class MultiPhaseTask {
        
        private static final int NUM_THREADS = 3;
    
        public static void main(String[] args) {
    
            // 注册线程可以通过构造函数的参数指定,也可以通过 phaser.register(); 方法注册线程。
            Phaser phaser = new Phaser(NUM_THREADS) {
          		@Override
          		protected boolean onAdvance(int phase, int registeredParties) {
            		System.out.println("第" + phase + "阶段完成");
            		return false; // 返回 true 会终止 phaser
          		}
        	};
    
            for (int i = 0; i < NUM_THREADS; i++) {
                // phaser.register(); // 如果上面造函数的参数没有指定,则启用这句话来注册线程
                Thread thread = new Thread(() -> {
                    
                    // 使用 for 循环来模拟3个阶段
                    for (int phase = 1; phase <= 3; phase++) {
                        // 这是第 phase 阶段
                        System.out.println("执行第 phase 阶段 任务...");               
                        phaser.arriveAndAwaitAdvance(); // 等待其他线程到达屏障点
                    }
                    
                    System.out.println("完成所有阶段");
                    // 解除线程的注册
                    phaser.arriveAndDeregister();
                });
                
                thread.start();
            }
        }
    }
    

三、测试示例1

  • 在这个示例中,我们创建了一个Phaser对象,重写了阶段回调函数。然后注册了3个线程(NUM_THREADS = 3)。在每个线程中通过循环设置5个阶段,每一个线程的每个阶段完成后调用phaser.arriveAndAwaitAdvance()方法等待其他线程。当所有线程都到达屏障点后,执行 Phaser 的 onAdvance 方法,然后它们会同时进行下一个阶段的任务。

    Phaser相对于CyclicBarrierCountDownLatch更为灵活和强大,适用于更复杂的多线程协作场景,但也需要更多的注意和谨慎来编写正确的同步逻辑。

    package top.yiqifu.study.p004_thread;
    
    import java.util.concurrent.Phaser;
    
    public class Test076_Phaser {
    
        private static final int NUM_THREADS = 3;
    
        public static void main(String[] args) {
    
            Phaser phaser = new Phaser() {
                @Override
                protected boolean onAdvance(int phase, int registeredParties) {
                    System.out.println("=====第" + phase + "阶段完成=====");
                    return false; // 返回 true 会终止 phaser
                }
            };
    
            for (int i = 1; i <= NUM_THREADS; i++) {
                phaser.register();// 注册线程
                Thread thread = new Thread(() -> {
    
                    String threadName = Thread.currentThread().getName();
                    // 使用 for 循环来模拟3个阶段
                    for (int phase = 0; phase < 5; phase++) {
                        // 这是第 phase 阶段
                        System.out.println(threadName + " 执行第"+ phase+"阶段任务...");
                        System.out.println(threadName +" 第"+ phase+"阶段完成,到达屏障点");
                        phaser.arriveAndAwaitAdvance(); // 等待其他线程到达屏障点
                    }
    
                    System.out.println(threadName +" 完成所有阶段");
                    System.out.println(threadName +" 解除线程的注册");
                    phaser.arriveAndDeregister();// 任务完成解除线程注册
                });
                thread.setName("线程"+i);
                thread.start();
            }
        }
    }
    
    

四、测试示例2

  • 下面示例中,假设三个玩家打游戏BOSS,每攻击3次为一个阶段,每到一个阶段进行30%回血。

    • 每个玩家每攻击一次自己扣血0~10%,Boss扣血0~15%。
    • 每个玩家每攻击三次,若没有击杀BOSS,则BOSS回血30%,若玩家没有挂,则继续挑战BOSS。
  • 这比前面CyclicBarrier的示例代码复杂一点点,以下是示例代码:

    如果你测试时,老是杀不掉,可以将 bossValue = Math.max(bossValue * 1.3, 100); 这个比例改小,如 bossValue = Math.max(bossValue * 1.15, 100);

    package top.yiqifu.study.p004_thread;
    
    
    import java.util.concurrent.Phaser;
    
    public class Test077_PhaserGame {
    
    
        private static final int PLAYER_COUNT = 3;
    
        public static void main(String[] args) {
            Phaser phaser = new Phaser(PLAYER_COUNT) {
                @Override
                protected boolean onAdvance(int phase, int registeredParties) {
                    // 三次没有杀死,Boss 回血 30%
                    GameBoss.Instance.enhancement(phase);
                    return false; // 返回 true 会终止 phaser
                }
            };
    
    
            for (int i = 0; i < PLAYER_COUNT; i++) {
                Thread thread = new Thread(() -> {
                    try {
                        double blood = 100; // 自己的血量
                        String threadName = Thread.currentThread().getName();
                        double attckVal = 0;
    
                        for (int j = 1; ; j++) {
                            Thread.sleep((int)(Math.random() * 1000 % 1000)); // 模拟玩家操作延时
                            attckVal = Math.random() * 100 % 15;
                            if(GameBoss.Instance.attack(threadName, attckVal, j)){
                                return;
                            }
                            blood -= Math.random() * 10 % 10;
                            if(blood <= 0){
                                // BOSS 没死,自己死了
                                System.out.println(threadName+ " GG");
                                break;
                            }
    
                            if(j % 3 == 0){
                                // 每攻击3次进入屏障,等待回血(你也可以刷怪)
                                phaser.arriveAndAwaitAdvance();
                            }
                        }
    
                        System.out.println(threadName+ " 离开");
                        phaser.arriveAndDeregister();
    
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
                thread.setName("玩家"+(i+1)+" ");
                thread.start();
            }
        }
    
    
        public static class GameBoss
        {
            public static  GameBoss Instance = new GameBoss();
    
            private GameBoss(){
    
            }
    
            private double bossValue = 100;
    
            public void enhancement(int phase){
                bossValue = Math.max(bossValue * 1.3, 100);
                System.out.println("第"+phase+"阶段回血,Boss 回血 30%");
            }
    
            public synchronized boolean attack(String player, double val, int times){
                if(bossValue < 0){
                    return true;
                }
    
                bossValue -= val;
                System.out.println(player+ "第"+times+"次发动攻击,伤害="+val);
    
                if(bossValue < 0){
                    System.out.println(player+ "击杀");
                    return true;
                }
    
                return false;
            }
        }
    }
    
    

    示例中, GameBoss 类的 attack 使用到了 synchronized 关键字,有关 synchronized 关键字请看 synchronized 使用说明

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

QIFU

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值