The class Phaser let you wait for a flexible amount of tasks executed in other threads. Use the method register to add a task you want to wait for. Call arrive to signal that a registered task is finished. And call awaitAdvance to wait till all registered tasks are finished. We will see how to use it by looking at a real-life example. But first, how does it work?
The class Phaser works in phases. You register tasks for all phases by calling the method register. You signal that a task is finished for this phase by calling arrive. When all registered tasks have arrived for this phase the Phaser starts a new phase and you can start over again. The following shows this for the first phase, phase zero:
Phaser phaser = new Phaser(); assertEquals( 0 , phaser.register() ); assertEquals( 0 , phaser.arrive() );
Both methods return the current phase, phase 0 in the example. By calling arrive, line 3 in the example above, the Phaser starts a new phase 1:
assertEquals( 1 , phaser.getPhase() );
And we can start over again calling arrive to start a new phase:
assertEquals( 1 , phaser.arrive() ); assertEquals( 2 , phaser.getPhase() );
The Phaser let us wait for other threads by the method awaitAdvance. awaitAdvance let a thread wait till the Phaser reaches a new phase. You call the awaitAdvance method with the current phase to wait for other threads:
phaser.awaitAdvance( phaser.getPhase() ); // waits for phaser.arrive() in other threads
awaitAdvance returns immediately if the current phase is not equal to the given phase value:
phaser.awaitAdvance( phaser.getPhase() + 1); // returns immediately
The Phaser let you deregister tasks by calling arriveAndDeregister:
phaser.arriveAndDeregister();
When all registered tasks are deregistered the Phaser gets terminated:
assertEquals( true , phaser.isTerminated() );
When the Phaser is terminated arrive and register have no effect and return a negative number. And the method await returns immediately. You can change this behavior by overriding the method onAdvance:
Phaser phaser = new Phaser() { protected boolean onAdvance(int phase, int parties) { return false; } }
By always returning false as in the example above, the Phaser can only be terminated by calling the method forceTermination explicitly.
The class ChangedFilesCollector from the IntelliJ community edition uses the Phaser to wait till all threads have reached a specific state. The used Phaser overrides the onAdvance method, to allow to reuse the Phaser when all tasks are deregistered:
private final Phaser myWorkersFinishedSync = new Phaser() { @Override protected boolean onAdvance(int phase, int registeredParties) { return false; } };
In the method processFilesInReadAction this Phaser is used to wait till all threads have finished their tasks:
private void processFilesInReadAction() { assert ApplicationManager.getApplication().isReadAccessAllowed(); myWorkersFinishedSync.register(); int phase = myWorkersFinishedSync.getPhase(); try { // other statements omitted } finally { myWorkersFinishedSync.arriveAndDeregister(); } myWorkersFinishedSync.awaitAdvance(phase); }
In line 3 the method register registers a new task. The variable phase remembers the current phase in line 4. In line 9 the method arriveAndDeregister signals that the task is done and deregisters the task. And in line 11 we wait for the other threads using the method awaitAdvance.
Java provides three classes to wait for other threads: Phaser, CountDownLatch, and CyclicBarrier. Use Phaser when you need to wait for a flexible amount of threads. When you need to wait for a fixed amount of tasks done in other threads use CountDownLatch instead. And use CyclicBarrier when you do the work and need to wait in the same threads for a fixed amount of threads.
This was the last java.util.concurrent class to wait for other threads. In the next blog post, we will look at the classes in the package java.util.concurrent.atomic.
I would be glad to hear from you about how you use Phaser in your application.
© 2020 vmlens Legal Notice Privacy Policy