一、概述
-
Phaser 也是Java并发编程中的一种同步辅助工具,用于线程之间的协调和同步。它提供了比CyclicBarrier和CountDownLatch更灵活和强大的功能,可以用于更复杂的多线程协作场景。
-
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相对于CyclicBarrier和CountDownLatch更为灵活和强大,适用于更复杂的多线程协作场景,但也需要更多的注意和谨慎来编写正确的同步逻辑。
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 使用说明