Phaser

网友投稿 703 2022-05-29

Phaser

Phaser介绍

Phaser这个单词是“移相器,相位器”的意思(好吧,笔者并不懂这是什么玩意,下方资料来自百度百科)。这个类是从JDK 1.7 中出现的。

移相器(Phaser)能够对波的相位进行调整的一种装置。任何传输介质对在其中传导的波动都会引入相移,这是早期模拟移相器的原理;现代电子技术发展后利用A/D、D/A转换实现了数字移相,顾名思义,它是一种不连续的移相技术,但特点是移相精度高。 移相器在雷达、导弹姿态控制、加速器、通信、仪器仪表甚至于音乐等领域都有着广泛的应用

Phaser类有点复杂,这里只介绍一些基本的用法和知识点。详情可以查看JDK文档,文档里有这个类非常详尽的介绍。

前面我们介绍了CyclicBarrier,可以发现它在构造方法里传入“任务总量”parties之后,就不能修改这个值了,并且每次调用await()方法也只能消耗一个parties计数。但Phaser可以动态地调整任务总量!

名词解释:

party:对应一个线程,数量可以通过register或者构造参数传入;

arrive:对应一个party的状态,初始时是unarrived,当调用arriveAndAwaitAdvance()或者 arriveAndDeregister()进入arrive状态,可以通过getUnarrivedParties()获取当前未到达的数量;

register:注册一个party,每一阶段必须所有注册的party都到达才能进入下一阶段;

deRegister:减少一个party。

phase:阶段,当所有注册的party都arrive之后,将会调用Phaser的onAdvance()方法来判断是否要进入下一阶段。

Phaser终止的两种途径,Phaser维护的线程执行完毕或者onAdvance()返回true 此外Phaser还能维护一个树状的层级关系,构造的时候new Phaser(parentPhaser),对于Task执行时间短的场景(竞争激烈),也就是说有大量的party, 那可以把每个Phaser的任务量设置较小,多个Phaser共同继承一个父Phaser。

Phasers with large numbers of parties that would otherwise experience heavy synchronization contention costs may instead be set up so that groups of sub-phasers share a common parent. This may greatly increase throughput even though it incurs greater per-operation overhead.

翻译:如果有大量的party,那许多线程可能同步的竞争成本比较高。所以可以拆分成多个子Phaser共享一个共同的父Phaser。这可能会大大增加吞吐量,即使它会带来更多的每次操作开销。

Phaser

Phaser案例

还是游戏的案例。假设我们游戏有三个关卡,但只有第一个关卡有新手教程,需要加载新手教程模块。但后面的第二个关卡和第三个关卡都不需要。我们可以用Phaser来做这个需求。

代码:

public class PhaserDemo { static class PreTaskThread implements Runnable { private String task; private Phaser phaser; public PreTaskThread(String task, Phaser phaser) { this.task = task; this.phaser = phaser; } @Override public void run() { for (int i = 1; i < 4; i++) { try { // 第二次关卡起不加载NPC,跳过 if (i >= 2 && "加载新手教程".equals(task)) { continue; } Random random = new Random(); Thread.sleep(random.nextInt(1000)); System.out.println(String.format("关卡%d,需要加载%d个模块,当前模块【%s】", i, phaser.getRegisteredParties(), task)); // 从第二个关卡起,不加载NPC if (i == 1 && "加载新手教程".equals(task)) { System.out.println("下次关卡移除加载【新手教程】模块"); phaser.arriveAndDeregister(); // 移除一个模块 } else { phaser.arriveAndAwaitAdvance(); } } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) { Phaser phaser = new Phaser(4) { @Override protected boolean onAdvance(int phase, int registeredParties) { System.out.println(String.format("第%d次关卡准备完成", phase + 1)); return phase == 3 || registeredParties == 0; } }; new Thread(new PreTaskThread("加载地图数据", phaser)).start(); new Thread(new PreTaskThread("加载人物模型", phaser)).start(); new Thread(new PreTaskThread("加载背景音乐", phaser)).start(); new Thread(new PreTaskThread("加载新手教程", phaser)).start(); } }

输出:

关卡1,需要加载4个模块,当前模块【加载背景音乐】

关卡1,需要加载4个模块,当前模块【加载新手教程】

下次关卡移除加载【新手教程】模块

关卡1,需要加载3个模块,当前模块【加载地图数据】

关卡1,需要加载3个模块,当前模块【加载人物模型】

第1次关卡准备完成

关卡2,需要加载3个模块,当前模块【加载地图数据】

关卡2,需要加载3个模块,当前模块【加载背景音乐】

关卡2,需要加载3个模块,当前模块【加载人物模型】

第2次关卡准备完成

关卡3,需要加载3个模块,当前模块【加载人物模型】

关卡3,需要加载3个模块,当前模块【加载地图数据】

关卡3,需要加载3个模块,当前模块【加载背景音乐】

第3次关卡准备完成

这里要注意关卡1的输出,在“加载新手教程”线程中调用了arriveAndDeregister()减少一个party之后,后面的线程使用getRegisteredParties()得到的是已经被修改后的parties了。但是当前这个阶段(phase),仍然是需要4个parties都arrive才触发屏障的。从下一个阶段开始,才需要3个parties都arrive就触发屏障。

另外Phaser类用来控制某个阶段的线程数量很有用,但它并在意这个阶段具体有哪些线程arrive,只要达到它当前阶段的parties值,就触发屏障。所以我这里的案例虽然制定了特定的线程(加载新手教程)来更直观地表述Phaser的功能,但是其实Phaser是没有分辨具体是哪个线程的功能的,它在意的只是数量,这一点需要读者注意。

Phaser原理

Phaser类的原理相比起来要复杂得多。它内部使用了两个基于Fork-Join框架的原子类辅助:

private final AtomicReference evenQ; private final AtomicReference oddQ; static final class QNode implements ForkJoinPool.ManagedBlocker { // 实现代码 }

有兴趣的读者可以去看看JDK源代码,这里不做过多叙述。

总的来说,CountDownLatch,CyclicBarrier,Phaser是一个比一个强大,但也一个比一个复杂。根据自己的业务需求合理选择即可。

5G游戏 任务调度

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:醒电ChargeSpot 携手合作藤原浩:共享充电宝设计也可质美
下一篇:ROS 2 ardent apalone安装和使用说明
相关文章