Java:jsr166y Phaser的教程/解释

Jas*_*n S 16 java concurrency phaser

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

必须要有一些很好的教程才能理解Phaser.我已经阅读了javadoc,但是我的眼睛茫然,因为为了真正理解javadoc你必须知道如何使用这些类.

有人有什么建议吗?

Joh*_*int 46

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

以下是CountdownLatch和CyclicBarrier的属性

注意:

  • 聚会的数量是说不同线程的另一种方式
  • 不可重用意味着您必须在重新使用之前创建屏障的新实例
  • 如果线程可以到达并且在不等待其他人的情况下继续工作或者可以等待所有线程完成,则可以推进屏障

CountdownLatch

  • 固定数量的政党
  • 不可恢复
  • 推进的(看看latch.countDown(); 推进的 latch.await(); 必须等待)

的CyclicBarrier

  • 固定数量的政党
  • 可重复使用
  • 不可推进

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

移相器

  • 动态党派数量
  • 可重复使用
  • 推进的

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

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

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

Java ExecutorService:awaitTermination所有递归创建的任务

灵活的CountDownLatch?


aro*_*oth 6

对于Phaser至少,我认为的JavaDoc提供了一个相当明确的解释.这是一个用于同步一批线程的类,在这个意义上你可以用a注册批处理中的每个线程Phaser,然后使用它Phaser来阻止它们直到批处理中的每个线程都通知了Phaser,此时任何被阻塞的线程都将开始执行.该等待/通知周期可以根据需要/要求一遍又一遍地重复.

他们的示例代码给出了一个合理的例子(虽然我非常不喜欢他们的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();
 } 
Run Code Online (Sandbox Code Playgroud)

这将设置Phaser一个注册计数为tasks.size() + 1,并且对于每个任务创建一个新的Thread,它将阻塞直到下一个前进Phaser(即tasks.size() + 1记录到达的时间),然后运行其相关任务.每个Thread创建的内容也会立即启动,因此Phasertasks.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();
 }
Run Code Online (Sandbox Code Playgroud)

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

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

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