By Markus Sprunck; Revision: 1.2; Status: final; Last Content Change: Jan 26, 2013;
One of the most interesting improvements of Java 7 is the better support of concurrency. With the JSR-166 Concurrency Utilities we get some very helpful improvements of concurrency. From my point of view the fork-join library has a very high potential for practical use in software engineering. Fork and join provides a very easy programming model for algorithms which can be implemented as recursive task. There are a lot of algorithms that can be implemented with these divide and conquer algorithms. During next years we will see an increasing number of cores in standard desktops, mobiles, notebooks and servers. The reason for this is easy - it is cheaper to add additional cores than to build a faster single processor. So, we will have to write more software which supports concurrency to take benefit of better hardware. My personal rule is: „You need a good reason to implement concurrency and if you have to do it be really careful.“ In the last years I saw more buggy implementations than working. This is the reason why I like the fork and join library. A clear programming model which implements the boiler plate code prevents you from errors. If you intend to use fork and join take some time to understand the behavior. Java ExampleThe example in file #1 and #2 is very similar to the example code in Java 7 documentation. In general, Fibonacci numbers with a recursive algorithm is not a good idea because there is a better linear solution (compare http://nayuki.eigenstate.org/page/fast-fibonacci-algorithms), but it is easier to implement and understand than others.Let's have a look at the example: // File #1: FibonacciTask.java [error handling, parameter validation and asserts removed]
// File #2: FibonacciTaskTest.java package com.sprunck.sample; import java.util.concurrent.ForkJoinPool; import junit.framework.Assert; import org.junit.Test; public class FibonacciTaskTest { // it makes no sense to create more threads than available cores (no speed improvement here) private static final int AVAILABLE_PROCESSORS = Runtime.getRuntime().availableProcessors(); // create thread pool private final ForkJoinPool pool = new ForkJoinPool(AVAILABLE_PROCESSORS); @Test public void testFibonacciArray() { // more test data: // http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fibonacci/fibtable.html long results[] = { 0L, 1L, 1L, 2L, 3L, 5L, 8L, 13L, 21L, 34L, 55L, 89L, 144L, 233L, 377L, 610L, 987L, 1597L, 2584L, 4181L, 6765L }; for (int inputValue = 0; inputValue < results.length; inputValue++) { final FibonacciTask task = new FibonacciTask(inputValue); System.out.print("Fibonacci(" + inputValue + ") = "); final long result = pool.invoke(task); System.out.println(result); Assert.assertEquals(results[inputValue], result); } } } Expected output of FibonacciTaskTest.java should look like this:
Fibonacci(0) = 0 Fibonacci(1) = 1 Fibonacci(2) = 1 Fibonacci(3) = 2 Fibonacci(4) = 3 Fibonacci(5) = 5 Fibonacci(6) = 8 Fibonacci(7) = 13 Fibonacci(8) = 21 Fibonacci(9) = 34 Fibonacci(10) = 55 Fibonacci(11) = 89 Fibonacci(12) = 144 Fibonacci(13) = 233 Fibonacci(14) = 377 Fibonacci(15) = 610 Fibonacci(16) = 987 Fibonacci(17) = 1597 Fibonacci(18) = 2584 Fibonacci(19) = 4181 Fibonacci(20) = 6765So far it is a simple and clear solution. No boiler plate code for concurrency, e.g. thread synchronization. Java Example with TracesBut I'd like to encourage you to have a deeper look in what happens in the solution. In files #3 and #4 you find an enhanced version of the same program. The only difference between the first and second version is some code to trace what happens during execution and a small slowTask() to simulate more realistic behavior.
// File #3: FibonacciTaskTraces.java
invoke Fibonacci(0) <- main result = 0 invoke Fibonacci(1) <- main result = 1 invoke Fibonacci(2) <- main result = 1 invoke Fibonacci(3) <- main | - Fibonacci(2) <- ForkJoinPool-1-worker-1 (fork) | - Fibonacci(1) <- ForkJoinPool-1-worker-1 | - Fibonacci(3) = 2 <- ForkJoinPool-1-worker-1 (join) result = 2 invoke Fibonacci(4) <- main | - Fibonacci(3) <- ForkJoinPool-1-worker-1 (fork) | - Fibonacci(2) <- ForkJoinPool-1-worker-1 | | - Fibonacci(2) <- ForkJoinPool-1-worker-2 (fork) | | - Fibonacci(1) <- ForkJoinPool-1-worker-2 | | - Fibonacci(3) = 2 <- ForkJoinPool-1-worker-2 (join) | - Fibonacci(4) = 3 <- ForkJoinPool-1-worker-1 (join) result = 3 invoke Fibonacci(5) <- main | - Fibonacci(4) <- ForkJoinPool-1-worker-1 (fork) | - Fibonacci(3) <- ForkJoinPool-1-worker-1 | | - Fibonacci(2) <- ForkJoinPool-1-worker-1 (fork) | | - Fibonacci(1) <- ForkJoinPool-1-worker-1 | | - Fibonacci(3) <- ForkJoinPool-1-worker-2 (fork) | | - Fibonacci(2) <- ForkJoinPool-1-worker-2 | | | - Fibonacci(2) <- ForkJoinPool-1-worker-2 (fork) | | | - Fibonacci(1) <- ForkJoinPool-1-worker-2 | | - Fibonacci(3) = 2 <- ForkJoinPool-1-worker-1 (join) | | | - Fibonacci(3) = 2 <- ForkJoinPool-1-worker-2 (join) | | - Fibonacci(4) = 3 <- ForkJoinPool-1-worker-2 (join) | - Fibonacci(5) = 5 <- ForkJoinPool-1-worker-1 (join) result = 5 invoke Fibonacci(6) <- main | - Fibonacci(5) <- ForkJoinPool-1-worker-1 (fork) | - Fibonacci(4) <- ForkJoinPool-1-worker-1 | | - Fibonacci(3) <- ForkJoinPool-1-worker-1 (fork) | | - Fibonacci(2) <- ForkJoinPool-1-worker-1 | | - Fibonacci(4) <- ForkJoinPool-1-worker-2 (fork) | | - Fibonacci(3) <- ForkJoinPool-1-worker-2 | | | - Fibonacci(2) <- ForkJoinPool-1-worker-2 (fork) | | | - Fibonacci(1) <- ForkJoinPool-1-worker-2 | | | - Fibonacci(2) <- ForkJoinPool-1-worker-1 (fork) | | | - Fibonacci(1) <- ForkJoinPool-1-worker-1 | | | - Fibonacci(3) = 2 <- ForkJoinPool-1-worker-2 (join) | | | - Fibonacci(3) = 2 <- ForkJoinPool-1-worker-1 (join) | | | - Fibonacci(3) <- ForkJoinPool-1-worker-2 (fork) | | | - Fibonacci(2) <- ForkJoinPool-1-worker-2 | | - Fibonacci(4) = 3 <- ForkJoinPool-1-worker-1 (join) | | | | - Fibonacci(2) <- ForkJoinPool-1-worker-2 (fork) | | | | - Fibonacci(1) <- ForkJoinPool-1-worker-2 | | | | - Fibonacci(3) = 2 <- ForkJoinPool-1-worker-2 (join) | | | - Fibonacci(4) = 3 <- ForkJoinPool-1-worker-2 (join) | | - Fibonacci(5) = 5 <- ForkJoinPool-1-worker-2 (join) | - Fibonacci(6) = 8 <- ForkJoinPool-1-worker-1 (join) result = 8 invoke Fibonacci(7) <- main | - Fibonacci(6) <- ForkJoinPool-1-worker-1 (fork) | - Fibonacci(5) <- ForkJoinPool-1-worker-1 | | - Fibonacci(4) <- ForkJoinPool-1-worker-1 (fork) | | - Fibonacci(3) <- ForkJoinPool-1-worker-1 | | | - Fibonacci(2) <- ForkJoinPool-1-worker-1 (fork) | | | - Fibonacci(1) <- ForkJoinPool-1-worker-1 | | - Fibonacci(5) <- ForkJoinPool-1-worker-2 (fork) | | - Fibonacci(4) <- ForkJoinPool-1-worker-2 | | | - Fibonacci(3) <- ForkJoinPool-1-worker-2 (fork) | | | - Fibonacci(2) <- ForkJoinPool-1-worker-2 | | | | - Fibonacci(2) <- ForkJoinPool-1-worker-2 (fork) | | | | - Fibonacci(1) <- ForkJoinPool-1-worker-2 | | | - Fibonacci(3) = 2 <- ForkJoinPool-1-worker-1 (join) | | | - Fibonacci(3) <- ForkJoinPool-1-worker-1 (fork) | | | - Fibonacci(2) <- ForkJoinPool-1-worker-1 | | | | - Fibonacci(3) = 2 <- ForkJoinPool-1-worker-2 (join) | | | | - Fibonacci(2) <- ForkJoinPool-1-worker-1 (fork) | | | | - Fibonacci(1) <- ForkJoinPool-1-worker-1 | | | - Fibonacci(4) = 3 <- ForkJoinPool-1-worker-2 (join) | | | - Fibonacci(4) <- ForkJoinPool-1-worker-2 (fork) | | | - Fibonacci(3) <- ForkJoinPool-1-worker-2 | | | | - Fibonacci(2) <- ForkJoinPool-1-worker-2 (fork) | | | | - Fibonacci(1) <- ForkJoinPool-1-worker-2 | | | | - Fibonacci(3) = 2 <- ForkJoinPool-1-worker-1 (join) | | | - Fibonacci(4) = 3 <- ForkJoinPool-1-worker-1 (join) | | | | - Fibonacci(3) = 2 <- ForkJoinPool-1-worker-2 (join) | | - Fibonacci(5) = 5 <- ForkJoinPool-1-worker-1 (join) | | | | - Fibonacci(3) <- ForkJoinPool-1-worker-2 (fork) | | | | - Fibonacci(2) <- ForkJoinPool-1-worker-2 | | | | | - Fibonacci(2) <- ForkJoinPool-1-worker-1 (fork) | | | | | - Fibonacci(1) <- ForkJoinPool-1-worker-1 | | | | | - Fibonacci(3) = 2 <- ForkJoinPool-1-worker-1 (join) | | | | - Fibonacci(4) = 3 <- ForkJoinPool-1-worker-2 (join) | | | - Fibonacci(5) = 5 <- ForkJoinPool-1-worker-2 (join) | | - Fibonacci(6) = 8 <- ForkJoinPool-1-worker-2 (join) | - Fibonacci(7) = 13 <- ForkJoinPool-1-worker-1 (join) result = 13
Recommendations
|