Twitter

Showing posts with label jdk7. Show all posts
Showing posts with label jdk7. Show all posts

Thursday, April 28, 2011

A peek into JDK7 - Java Phaser: Taking concurrency to the next level

Some days ago there was a discussion with one of my colleague regarding synchronization points between several Java threads. Of course there are CountDownLatch and CyclicBarrier for solving this kind of problems. But even with them we need to know how may threads are we going to start before hand. Because both  CountDownLatch and CyclicBarrier expects the number of parties to be synchronized as the constructor argument.

new CountDownLatch(count);
new CyclicBarrier(parties);

Then I came across Phaser. In case of Phaser you don't need to give the number of parties as an constructor argument. 

new Phaser();

It is one of the concurrency features coming in Java 7. It is designed in such a way that a thread that needs to be in sync with other threads can register() themselves. Like a CyclicBarrier a Phaser can be reused (during several phases of the task). For this use arriveAndAwaitAdvance(), i.e after completing each phase you arrive and wait for all the other threads and once they all reach you advance (move) to the next phase.

In this example code you see 3 threads being started. Once a task begins the thread will register itself with the Phaser. After completing each phase of the task each thread will tell the Phaser that it has completed a certain phase by calling  arrive() and then wait for the others with awaitAdvance(). And at any phase one party can ask the Phaser for the number of parties those have not yet arrived be calling getUnarrivedParties().

@Override
  public void run() {
   
   _phaser.register(); // register on the fly

   // First phase
   doSomeWork();

   int arr1 = _phaser.arrive(); // let the phaser know that you have arrived this point of the task
   int unarrivedParties1 = _phaser.getUnarrivedParties(); // ask the phaser how many parties are not yet here
   
   if (_phaser.getPhase() == arr1) {
    System.out.println(_taskId + " completed phase " + arr1 + " and waiting arraival of " + 
         unarrivedParties1 + " threads so that it can enter phase " + (arr1 + 1));
   }
   else {
    System.out.println(_taskId + " completed phase " + (arr1) + 
         " (the last one to reach) and now all tasks will proceed to phase " + (arr1 + 1));
   }
   
   _phaser.awaitAdvance(arr1); // be in sync with other threads

   
   
   // Second phase
   doSomeWork();

   int arr2 = _phaser.arrive(); // let the phaser know that you have arrived this point of the task
   int unarrivedParties2 = _phaser.getUnarrivedParties(); // ask the phaser how many parties are not yet here

   if (_phaser.getPhase() == arr2) {
    System.out.println(_taskId + " completed phase " + arr2 + " and waiting arraival of " + 
         unarrivedParties2 + " threads so that it can enter phase " + (arr2 + 1));
   }
   else {
    System.out.println(_taskId + " completed phase " + (arr2) + 
         " (the last one to reach) and now all tasks will proceed to phase " + (arr2 + 1));
   }
   _phaser.awaitAdvance(arr2); // be in sync with other threads

   // and so on ...
   
   // at some point a task could de-register itself from the phaser
   _phaser.arriveAndDeregister(); // de-registered threads is not considered need not be in sync with the other threads any more
   

  }

And this is the output:
Thread_1 completed phase 0 and waiting arraival of 2 threads so that it can enter phase 1
Thread_2 completed phase 0 and waiting arraival of 1 threads so that it can enter phase 1
Thread_0 completed phase 0 (the last one to reach) and now all tasks will proceed to phase 1
Thread_0 completed phase 1 and waiting arraival of 2 threads so that it can enter phase 2
Thread_1 completed phase 1 and waiting arraival of 1 threads so that it can enter phase 2
Thread_2 completed phase 1 (the last one to reach) and now all tasks will proceed to phase 2

Saturday, April 2, 2011

Visualizing FJ (Fork And Join) Framework

Earlier I made a post on the new FJ framework in JDK7 here. By the time there was no tool to visualize how FJ works. Today I came across a link where you can download a jar which helps us to visualize FJ.

You can download the jar file from the original link or here from my Dropbox.

Here is a sample screen shop of the jar demo, which sorts numbers from 1-32.

Have fun.


Thursday, March 11, 2010

A peek into JDK7 - ForkJoinTask example (RecursiveAction example, Forkjoinpool example)

Consider tasks like sorting an array, doing a complex math on each and every element in an array. Eg. we want to increment by 1 all the elements in the array {0,1,2,3,4,5,6,7,8,9}.

The simplest way is to loop over the entire array and do array[i]=array[i]+1. However this will run in a single thread.

But what if we can take advantage of multi-core CPUs, i.e. break the array into two halves and give it to two threads. So that the first thread operates on the left-half (thereby modifying the array entries to {1,2,3,4,5,......}) of the array whereas the second thread operates on the second-half of the array (thereby modifying the array entries to {......,6,7,8,9,10}).

The ....s means that the corresponding thread doesn't know what is there. It doesn't have to care. It is not part of its job!

This is where the JDK7's ForkJoinTask comes into the play. We give a complex task to be executed. Along with that we also have to specify a threshold. If the task's size is greater than the threshold then the task divides itself and fork()s them and wait for them to finish by join()ing. Hence the name ForkJoinTask. There are two implementation of ForkJoinTask - RecursiveAction and RecursiveTask.

Here is an example. The applyAlgorithm() is the CPU intensive method where each element in the array is modified. When the array is bigger than 5000 (threshold), then the array is divided into two and the two new arrays are handled in parallel by the threads available in the ForkJoinPool.

Following are the results from 2 different machines. One on a 16 core machine and another on a dual core machine. In both the cases the parallel execution is well ahead the single threaded numbers.

You can download the java code here.

1. On a 16 core machine
myServer $ java -cp test/jsr166y.jar:. ForkJoinAlgoritmicTask
Number of processor available: 16
Array size: 10000000
Treshhold: 5000
Number of runs: 5
 
Parallel processing time: 198
Parallel processing time: 69
Parallel processing time: 64
Parallel processing time: 61
Parallel processing time: 59

Number of steals: 579

Sequential processing time: 438
Sequential processing time: 437
Sequential processing time: 436
Sequential processing time: 436
Sequential processing time: 437

2. On a 2 core machine
muruga-Study$java -cp jsr166y.jar:. -Xms1G -Xmx1G ForkJoinAlgoritmicTask
Number of processor available: 2
Array size: 10000000
Treshhold: 5000
Number of runs: 5

Parallel processing time: 227
Parallel processing time: 206
Parallel processing time: 226
Parallel processing time: 203
Parallel processing time: 208
Number of steals: 12

Sequential processing time: 385
Sequential processing time: 385
Sequential processing time: 385
Sequential processing time: 385
Sequential processing time: 385