Waiting for Tasks With Phaser
Need to wait for other threads? Let's dive into the Phaser class and its assorted methods like arrive and register to see how it can work for you.
Join the DZone community and get the full member experience.
Join For FreeThe class Phaser lets you wait for a flexible number 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 until 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?
How Does Phaser 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 lets us wait for other threads via the method awaitAdvance. awaitAdvance lets a thread wait until 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
Deregistering Tasks and the Terminal State
The Phaser lets 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.
Example: Waiting for Other Threads
The class ChangedFilesCollector from the IntelliJ community edition uses the Phaser to wait until 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 until 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 8, the method arriveAndDeregister signals that the task is done and deregisters the task. And in line 10, we wait for the other threads using the method awaitAdvance.
Other Classes to Wait for Threads
Java provides three classes to wait for other threads: Phaser, CountDownLatch, and CyclicBarrier. Use Phaser when you need to wait for a flexible number 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.
Next Steps
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 applications.
Published at DZone with permission of Thomas Krieger, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments