Java:jsr166y Phaser 的教程/说明

2022-09-03 03:22:39

这个问题是两年前问过的,但它提到的资源要么不是很有帮助(恕我直言),要么链接不再有效。

必须有一些好的教程来理解Phaser。我读过javadoc,但我的眼睛瞪大了,因为为了真正理解javadoc,你必须知道这些类应该如何使用。

有人有什么建议吗?


答案 1

对于Phaser,我回答了几个问题。看到它们可能有助于理解它们的应用程序。它们在底部链接。但是,要了解Phaser的作用以及为什么它有用,重要的是要知道它解决了什么问题。

以下是倒计时闩锁和循环巴里尔的属性

注意:

  • 参与方的数量是另一种说法,即不同线程的数量
  • 不可重用意味着在重用之前,您必须创建屏障的新实例
  • 如果线程可以到达并继续工作而无需等待其他人,或者可以等待所有线程完成,则障碍是可以提前的

倒计时

  • 固定的参与方数量
  • 不可补发
  • 可先得(看可先行必等latch.countDown();latch.await(); )

循环巴里耶

  • 固定的参与方数量
  • 可 重用
  • 不可提前

因此,CountdownLatch是不可重用的,您必须每次创建一个新实例,但可以免费使用。CylicBarrier可以重复使用,但所有线程都必须等待各方到达屏障。

相位

  • 动态参与方数量
  • 可 重用
  • 可高级

当一个线程想要被相位器知道时,当线程到达它们调用的屏障时,它们会调用,这里是可以提前的。如果线程想要等待所有已注册的任务完成phaser.register()phaser.arrive()phaser.arriveAndAwaitAdvance()

还有一个阶段的概念,在该阶段中,线程可以等待可能尚未完成的其他操作的完成。一旦所有线程到达相位器的屏障,就会创建一个新阶段(增量为1)。

你可以看看我的其他答案,也许会有所帮助:

Java ExecutorService: await所有递归创建的任务的终结

灵活的倒计时程序?


答案 2

至少,我认为JavaDoc提供了一个相当清晰的解释。这是一个用于同步一批线程的类,从某种意义上说,您可以使用 a 注册批处理中的每个线程,然后使用 to have them 块,直到批处理中的每个线程都通知了 ,此时任何被阻止的线程都将开始执行。这种等待/通知周期可以根据需要/要求一遍又一遍地重复。PhaserPhaserPhaserPhaser

他们的示例代码给出了一个合理的例子(尽管我非常不喜欢他们的2个字符的缩进风格):

void runTasks(List<Runnable> tasks) {
   final Phaser phaser = new Phaser(1); // "1" to register self
   // create and start threads
   for (final Runnable task : tasks) {
     phaser.register();
     new Thread() {
       public void run() {
         phaser.arriveAndAwaitAdvance(); // await all creation
         task.run();
       }
     }.start();
   }

   // allow threads to start and deregister self
   phaser.arriveAndDeregister();
 } 

这将设置一个注册计数为 的 ,并为每个任务创建一个新的任务,该任务将阻塞直到下一个提前(即记录到达的时间),然后运行其关联的任务。创建的每个内容也会立即启动,因此记录到达的循环中会出来。Phasertasks.size() + 1ThreadPhasertasks.size() + 1ThreadPhasertasks.size()

最终调用将记录最终到达,并递减注册计数,使其现在等于 。这会导致 前进,这实际上允许所有任务同时开始运行。这可以通过执行如下操作来重复:phaser.arriveAndDeregister()tasks.size()Phaser

void runTasks(List<Runnable> tasks) {
   final Phaser phaser = new Phaser(1); // "1" to register self
   // create and start threads
   for (final Runnable task : tasks) {
     phaser.register();
     new Thread() {
       public void run() {
         while (true) {
           phaser.arriveAndAwaitAdvance(); // await all creation
           task.run();
         }
       }
     }.start();
   }

   // allow threads to start and deregister self
   phaser.arriveAndDeregister();
 }

...这与以前相同,只是添加了一个循环,导致任务重复运行。因为每次迭代调用,所以任务线程的执行将同步,使得 task-0 不会开始其第二次迭代,直到所有其他任务都完成其第一次迭代并通知 该任务已准备好开始其第二次迭代。phaser.arriveAndAwaitAdvance()Phaser

如果您正在运行的任务在执行时间上存在很大差异,并且您希望确保较快的线程不会与较慢的线程不同步,这可能很有用。

对于可能的实际应用程序,请考虑运行单独图形和物理线程的游戏。如果图形线程卡在第 6 帧上,则您不希望物理线程计算第 100 帧的数据,并且使用一种可能的方法可以确保图形和物理线程始终在同一帧上同时工作(并且如果一个线程明显慢于另一个线程,则更快的线程会优雅地产生 CPU 资源,以便希望较慢的线程可以捕获上来更快)。Phaser


推荐