Java 7 : More concurrency

With Java 7 (Dolphin), we’ll have some concurrency and collections updates with the JSR166y, extension of the JSR166 of Doug Lea.

In this post, we’ll see the most important news :

  • Fork/Join Framework
  • TrasnferQueue<E>
  • ThreadLocalRandom

Fork/Join Framework

The most important improvement is a new Fork/Join Framework. Fork/Join is basically the parralel version of the divide-and-conquer algorithm resolution. Here is the typical form of that problems (taken from Doug Lea) :

Result solve(Problem problem) {
    if (problem is small)
        directly solve problem
    else {
        split problem into independent parts
        fork new subtasks to solve each part
        join all subtasks
        compose result from subresults
    }
}

Java 7 provide a new class ForkJoinPool to run ForkJoinTask. A ForkJoinTask is lighter than a thread. If you have a lot of ForkJoinTask, you can host them with a smallest number of threads. Two implementations of ForkJoinTask are provided :

  • RecursiveAction : A recursive resultless ForkJoinTask
  • RecursiveTask<E> : A recursive ForkJoinTask that return an object of type E

Of course, you can also directly use the ForkJoinTask class but the recursive actions are enough in almost all the cases.

From a ForkJoinTask you can invoke other task (fork them) using invokeAll methods.

So, now that we have covered the main concepts of this framework, we could start with a little example (directly taken from Javadoc build 87). We’ll use divide and conquer to increment all the elements of an array. To know if the problem is small enough to solve it directly, we’ll use a threshold representing the number of elements that we can increment directly. If we have more elements than the threshold, we will fork in two task otherwise, we’ll compute directly the incrementation on the array. So here is our task :

public class IncrementTask extends RecursiveAction {
   private final long[] array;
   private final int low;
   private final int high;

   private static final int THRESHOLD = 5000;

   public IncrementTask(long[] array, int low, int high) {
      super();

      this.array = array;
      this.low = low;
      this.high= high;
   }

   @Override
   protected void compute() {
      if (high - low < THRESHOLD) {
           for (int i = low; i < high; ++i){
              array[i]++;
           }
        } else {
           int mid = (low + high) >>> 1;

           invokeAll(new IncrementTask(array, low, mid), new IncrementTask(array, mid, high));
      }
   }
}

And you can launch that on an array using ForkJoinPool :

RecursiveAction mainTask = new IncrementTask (anArray, 0, anArray.length);
ForkJoinPool mainPool = new ForkJoinPool();

mainPool.invoke(mainTask

e>
All the elements of the array will be incremented. Depending on the size of the array and of the threshold, the problem will be divided in several sub problems and all these task will be managed by the ForkJoinPool.

You can also make action that return something. By example, we can compute the sum of all the elements of an array :

public class SumTask extends RecursiveTask {
   private final long[] array;
   private final int low;
   private final int high;

   private static final int THRESHOLD = 5000;

   public SumTask(long[] array, int low, int high) {
      super();

      this.array = array;
      this.low = low;
      this.high= high;
   }

   @Override
   protected Long compute() {
      if (high - low < THRESHOLD) {
          long sum = 0;

          for (int i = low; i < high; ++i){
              sum += array[i];
           }

           return sum;
       } else {
           int mid = (low + high) >>> 1;

          RecursiveTask left = new SumTask(array, low, mid);
          RecursiveTask right = new SumTask(array, mid, high);

          right.fork();

          return left.compute() + right.join();
      }
   }
}

And you can use it like that :

RecursiveTask sumTask = new SumTask(anArray, 0, anArray.length);
ForkJoinPool mainPool = new ForkJoinPool();

Long sum = mainPool.invoke(sumTask);

I think it’s a clean way to solve big problems with divide-and-conquer.

You can also imagine others ways to divide the problems. An example is to compute the THRESOLD left elements in the task and create a new task to compute the right elements. With that, we create less tasks, but it depends on the context and on the problems. In practive, you’ll have  normally more complex problems but if you can find a way to divide the problems, you can use that new framework and have a very clean code.

TransferQueue<E>

A new interesting collection. This collection is a blocking queue especially made for producers/consumers. With that kind of queue, the producers can await for receipt of by the consumers with a new transfer(E) method or like normal queue without waiting for receipt with the put(E) method. It’s also possible to make a transfer with timeout with the tryTransfer method. There is no change in the consumer part, you always use take() to get an element and waiting for an element. You’ve also access to the number of waiting consumer with the getWaitingConsumerCount().

The implementation to use is the LinkedTransferQueue<E> based on linked nodes. The elements are ordered with FIFO. Here are some methods you can use with that new collection :

TransferQueue<String> transfer = new LinkedTransferQueue<String>();

transfer.transfer("Hello"); //Wait for a consumer

if(transfer.tryTransfer("World")){//Don't wait for a consumer
    //The element has been transfered to a consumer
} else {
    //There were no waiting consumer. The element has not been enqueued.
}

boolean transfered = transfer.tryTransfer("Goodbye", 5, TimeUnit.SECONDS);

while(transfer.hasWaitingConsumer()){
    //There is at least one consumer waiting for a transfer
}

It’s also an interesting stuff. Useful by example in the case of message passing.

ThreadLocalRandom

A really simple but useful enhancement is the add of the ThreadLocalRandom class. This class is a random number generator linked to the current Thread. It seems that if you use this generator from two different thread, you will have two different random generators. The generator is initialized with a generated seed that you cannot modify (setSeed() throws an UnsupportedOperationException).

You can use that class like that :

long l = ThreadLocalRandom.current().nextLong(22L);

If you always use this form, you have the guarantee that the random generator will never be shared between two threads. Moreover, this new class provide  methods to generate a bounded numbers. By example, to generate a pseudo-random number between 10, inclusive and 33, exclusive, you can type :

int i = ThreadLocalRandom.current().nextInt(10, 33);

This is a little improvement but really useful, i think.

So here we are. I’ve covered the main features added on Java 7 for concurrency. I hope you find that stuff interesting and that discovering this features will help you to make concurrent programming in Java 7.

 

  • http://eclecticprogrammer.com David Sheth

    Hi,

    Nice post. As multicore becomes more prevalent, I think we’ll see more interest in Java’s concurrency offerings.

    A couple of notes:

    1. In your fork join example, you fork the left task, and then call compute on the left task while calling join on the right task. That seems incorrect…you should probably fork the right task, then call compute on the left task while calling join on the right task. However,

    2. You might also want to mention that the other major concurrency feature to be added to java 7 will be closures of some sort. Java 7 was delayed to add closures, and closures are being specifically added to help with concurrency.

    • Baptiste Wicht

      You’re right, that was an error. It’s edited. Thanks. The closures will effectively improve the concurrency system but also others part of the language, like GUI, listeners, … So it’s not really the post to talk about that, I think.

  • http://eclecticprogrammer.com David Sheth

    Hi,

    Nice post. As multicore becomes more prevalent, I think we’ll see more interest in Java’s concurrency offerings.

    A couple of notes:

    1. In your fork join example, you fork the left task, and then call compute on the left task while calling join on the right task. That seems incorrect…you should probably fork the right task, then call compute on the left task while calling join on the right task. However,

    2. You might also want to mention that the other major concurrency feature to be added to java 7 will be closures of some sort. Java 7 was delayed to add closures, and closures are being specifically added to help with concurrency.

    • Baptiste Wicht

      You’re right, that was an error. It’s edited. Thanks. The closures will effectively improve the concurrency system but also others part of the language, like GUI, listeners, … So it’s not really the post to talk about that, I think.

  • Gili

    Closures are not guaranteed to be in Java7. There is a strong push for their inclusion but it’s nothing more than a proposal. Java7 was delayed due to the Sun/Oracle merger throwing a wrench on working schedules (people leaving and joining the company), not because of Closures. At least, that’s what I remember reading…

  • Gili

    Closures are not guaranteed to be in Java7. There is a strong push for their inclusion but it’s nothing more than a proposal. Java7 was delayed due to the Sun/Oracle merger throwing a wrench on working schedules (people leaving and joining the company), not because of Closures. At least, that’s what I remember reading…

  • http://www.insaneprogramming.be Lieven Doclo

    Nope, closures will probably be included in Java 7. See http://openjdk.java.net/projects/jdk7/features (Project Lambda). Granted, they won’t be as extensive as some people hope them to be, but I think they might make a valuable asset.

  • http://www.insaneprogramming.be Lieven Doclo

    Nope, closures will probably be included in Java 7. See http://openjdk.java.net/projects/jdk7/features (Project Lambda). Granted, they won’t be as extensive as some people hope them to be, but I think they might make a valuable asset.

  • http://www.stupidjavatricks.com Tony

    Good post. Merge sort would make a great example as well. I’m looking forward to seeing how closures will further simplify this new API.

  • http://www.stupidjavatricks.com Tony

    Good post. Merge sort would make a great example as well. I’m looking forward to seeing how closures will further simplify this new API.

  • azila

    Good post again :) I really like your Java 7 posts. They give a nice overview of the new features & are easy to understand.

  • azila

    Good post again :) I really like your Java 7 posts. They give a nice overview of the new features & are easy to understand.

  • http://fotki.com/dmitri_don dmitri don

    Thanks for intro to new features of java 7! Your examples are very easy to read! Fix this line: if (high – low < THRESHOLD) {

    –Dmitri

    • Baptiste Wicht

      Thanks dmitri, that’s fixed :)

  • http://fotki.com/dmitri_don dmitri don

    Thanks for intro to new features of java 7! Your examples are very easy to read! Fix this line: if (high – low < THRESHOLD) {

    –Dmitri

    • Baptiste Wicht

      Thanks dmitri, that’s fixed :)

  • Bob

    To properly run the IncrementTask, you need to specify the following:

    RecursiveAction mainTask = new IncrementTask (anArray, 0, anArray.length/*-1*/);
    ForkJoinPool mainPool = new ForkJoinPool();

    mainPool.invoke(mainTask);

    I commented out the “-1″. Otherwise, all the values in the array are incremented except the last one.

    Bob

    • Bob

      Same thing with SumTask.

    • Baptiste Wicht

      You’re right, thanks.

      Sorry for all the errors i made in these codes.

      • Bob

        You copied it from the Javadocs. You can blame Sun/Oracle :)

  • Bob

    To properly run the IncrementTask, you need to specify the following:

    RecursiveAction mainTask = new IncrementTask (anArray, 0, anArray.length/*-1*/);
    ForkJoinPool mainPool = new ForkJoinPool();

    mainPool.invoke(mainTask);

    I commented out the “-1″. Otherwise, all the values in the array are incremented except the last one.

    Bob

    • Bob

      Same thing with SumTask.

    • Baptiste Wicht

      You’re right, thanks.

      Sorry for all the errors i made in these codes.

      • Bob

        You copied it from the Javadocs. You can blame Sun/Oracle :)

  • DM

    Would you say that ForkJoinPool and Apple’s Grand Central Dispatch are inspired by the say idea: to make parallelism and synchronization easier for general use?

    • Baptiste Wicht

      I don’t know well Apple’s Grand Central Dispatch, but from what i’ve read about it, yes, this is the same idea, but at different level. Grand Central Dispatch operate directly at OS level and Fork/Join framework operate directly on the virtual machine so independently of the OS.

      • http://eclecticprogrammer.com David Sheth

        I think Grand Central Dispatch is actually more analagous to the Java 5′s Executor framework, in that tasks are placed on a single fifo queue, and threads read tasks from that queue. If a task needs to be broken down into smaller tasks, the new child tasks get placed at the end of the fifo queue. This is different from Java’s implementation of Fork/Join, in which each thread has its own Deque, a task can be broken down into a smaller tasks and placed on the front of the deque, and idle threads can steal work from the end of the deque of a thread that still has work (called Work Stealing). There are two primary advantages of Fork/Join with Work Stealing over the Executor or Grand Central way of doing things:

        1. Since each thread primarily works from its own queue, there is less contention that there would be on the single fifo queue.
        2. When a task is decomposed into smaller tasks and then placed on the front of the queue, that means that the child tasks will probably be performed by the same thread that did the decomposing in the first place. Due to cache effects, this can be quite a performance win.

        Work Stealing Queues are implemented not only by Java’s Fork-Join, but also by Intel’s Thread Building Blocks and a pair of libraries Microsoft released last week as part of Visual Studio 2010–one for C++ (called the Concurrency Runtime), and one for .NET. (an enhancement to the already existing Task Parallel library…see http://blogs.msdn.com/pfxteam/archive/2009/07/07/9822857.aspx for details).

        • Baptiste Wicht

          Thanks for this explanation ! Really interesting.

          • Baptiste Wicht

            That’s interesting, thanks.

            But I’m not sure I understand why you cannot do that with the fork/join framework of Java 7. You can create a main for all the components of the problems and then join them all to compute the final results, no ?

          • http://eclecticprogrammer.com David Sheth

            Yes, you can do that in fork-join–as you said, you’d just create tasks, and pass them to the fork-join pool. If those tasks don’t happen to create child tasks, then fork-join behaves almost like executor.

  • DM

    Would you say that ForkJoinPool and Apple’s Grand Central Dispatch are inspired by the say idea: to make parallelism and synchronization easier for general use?

    • Baptiste Wicht

      I don’t know well Apple’s Grand Central Dispatch, but from what i’ve read about it, yes, this is the same idea, but at different level. Grand Central Dispatch operate directly at OS level and Fork/Join framework operate directly on the virtual machine so independently of the OS.

      • http://eclecticprogrammer.com David Sheth

        I think Grand Central Dispatch is actually more analagous to the Java 5′s Executor framework, in that tasks are placed on a single fifo queue, and threads read tasks from that queue. If a task needs to be broken down into smaller tasks, the new child tasks get placed at the end of the fifo queue. This is different from Java’s implementation of Fork/Join, in which each thread has its own Deque, a task can be broken down into a smaller tasks and placed on the front of the deque, and idle threads can steal work from the end of the deque of a thread that still has work (called Work Stealing). There are two primary advantages of Fork/Join with Work Stealing over the Executor or Grand Central way of doing things:

        1. Since each thread primarily works from its own queue, there is less contention that there would be on the single fifo queue.
        2. When a task is decomposed into smaller tasks and then placed on the front of the queue, that means that the child tasks will probably be performed by the same thread that did the decomposing in the first place. Due to cache effects, this can be quite a performance win.

        Work Stealing Queues are implemented not only by Java’s Fork-Join, but also by Intel’s Thread Building Blocks and a pair of libraries Microsoft released last week as part of Visual Studio 2010–one for C++ (called the Concurrency Runtime), and one for .NET. (an enhancement to the already existing Task Parallel library…see http://blogs.msdn.com/pfxteam/archive/2009/07/07/9822857.aspx for details).

        • Baptiste Wicht

          Thanks for this explanation ! Really interesting.

          • Baptiste Wicht

            That’s interesting, thanks.

            But I’m not sure I understand why you cannot do that with the fork/join framework of Java 7. You can create a main for all the components of the problems and then join them all to compute the final results, no ?

          • http://eclecticprogrammer.com David Sheth

            Yes, you can do that in fork-join–as you said, you’d just create tasks, and pass them to the fork-join pool. If those tasks don’t happen to create child tasks, then fork-join behaves almost like executor.

  • Pingback: links for 2010-04-15 « Daniel Harrison's Personal Blog

  • http://coopsoft.com Ed Harned

    Fork-Join for Java7 is only for number-crunching (compute intensive) applications.

    See this article for a more robust version of Fork-Join for everyday applications.

    Fork-Join Development in Java SE
    http://www.coopsoft.com/ar/ForkJoinArticle.html

  • http://coopsoft.com Ed Harned

    Fork-Join for Java7 is only for number-crunching (compute intensive) applications.

    See this article for a more robust version of Fork-Join for everyday applications.

    Fork-Join Development in Java SE
    http://www.coopsoft.com/ar/ForkJoinArticle.html

  • http://www.terrazzocleaning.net Terrazzo Floor Ft.Lauderdale

    Java 7 : More concurrency

  • http://www.findprefab.com/find-prefab-homes/panelized-sip Panelized Homes

    Your examples are very easy to read! Thanks for the site.

  • Mattias De Wael

    Hi!
    I am looking for a good tool to profile/analyze my applications using jaca FJ. All profilers I used so far slow my application down to much to be able te reason correcly about it…
    Anyone an idea?

    MdW

    • Anonymous

      Hi,

      Did you try VisualVM ? In my case it didn’t slow to much my application.

  • Darline Hettwer
  • Pingback: Devoxx - The Well-Grounded Java Developer | Kevin Van Ransbeeck Portfolio