How it works...

The program starts creating a Phaser object that will control the synchronization of the threads at the end of each phase. The constructor of Phaser receives the number of participants as a parameter. In our case, Phaser has three participants. This number indicates Phaser the number of threads that need to execute the arriveAndAwaitAdvance() method before Phaser could change the phase and wake up the threads that have been sleeping.

Once Phaser has been created, we launch three threads that are executed using three different FileSearch objects.

In this example, we use paths of the Windows operating system. If you work with another operating system, modify the paths to adapt them to existing paths in your environment, such as /var/log, or similar.

The first instruction in the run() method of this FileSearch object is a call to the arriveAndAwaitAdvance() method of the Phaser object. As mentioned earlier, Phaser knows the number of threads that we want to synchronize. When a thread calls this method, Phaser decreases the number of threads that have to finalize the actual phase and puts this thread to sleep until all the remaining threads finish this phase. Calling this method at the beginning of the run() method ensures that none of the FileSearch threads begin their job until all the threads are created.

At the end of phase one and phase two, we check whether the phase has generated results and the list with the results has elements or the phase hasn't generated results and the list is empty. In the first case, the checkResults() method calls arriveAndAwaitAdvance() as explained earlier. In the second case, if the list is empty, there's no point in the thread continuing with its execution, so it ends its execution. But you have to notify the Phaser object that there will be one less participant. For this, we used arriveAndDeregister(). This notifies phaser that the thread has finished the actual phase, but it won't participate in future phases, therefore, phaser won't have to wait for it to continue.

At the end of the phase three implemented in the showInfo() method, there is a call to the arriveAndAwaitAdvance() method of phaser. With this call, we guarantee that all the threads finish at the same time. When this method ends its execution, there is a call to the arriveAndDeregister() method of phaser. With this call, we deregister the threads of phaser, as explained before, so when all the threads finish, phaser will have zero participants.

Finally, the main() method waits for the completion of the three threads and calls the isTerminated() method of phaser. When phaser has zero participants, it enters the so-called termination state, and this method returns true. As we deregister all the threads of phaser, it will be in the termination state, and this call will print true to the console.

A Phaser object can be in two states:

  • Active: Phaser enters this state when it accepts the registration of new participants and its synchronization at the end of each phase. In this state, Phaser works as it has been explained in this recipe. This state is not mentioned in the Java concurrency API.
  • Termination: By default, Phaser enters this state when all the participants in Phaser have been deregistered, which means it has zero participants. Further, Phaser is in the termination state when the method onAdvance() returns true. If you override this method, you can change the default behavior. When Phaser is in this state, the synchronization method arriveAndAwaitAdvance() returns immediately without doing any synchronization operation.

A notable feature of the Phaser class is that you haven't had to control any exception from the methods related to phaser. Unlike other synchronization utilities, threads that are sleeping in phaser don't respond to interruption events and don't throw an InterruptedException exception. There is only one exception, which is explained in the next section.

The following screenshot shows the results of one execution of the example:

It shows the first two phases of the execution. You can see how the Apps thread finishes its execution in phase two because its results list is empty. When you execute the example, you will see how some threads finish a phase before the rest and how they wait until all have finished one phase before continuing with the rest.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.22.160