Java Thread Pool class #
Managing the threads yourself is not always necessary and often too expensive for smaller tasks. There is in Java the so-called thread pool, to which tasks can be passed. These tasks are executed and a Future<result>
is returned immediately. This future will then eventually contain the result of the task or null
if no result was requested.
ForkJoinPool threadPool = new ForkJoinPool();
Future<Integer> future = threadPool.submit(() -> {
int value = ...;
// long calculation
return value;
});
int result = future.get();
However, a RecursiveTask<result>
or RecursiveAction
can also be passed to the ForkJoinPool.
class CountTask extends RecursiveTask<Integer> {
// Constructor
@Override
protected Integer compute() {
// if no or single element => return result
// split into two parts
CountTask left = new CountTask(lower, middle);
CountTask right = new CountTask(middle, upper);
left.fork(); right.fork();
return right.join() + left.join();
}
}
int result = threadPool.invoke(new CountTask(2, N)); //invoke blocked and do not submit
The advantage of the above structure is the possible recursion. However, it must not be assumed that tasks are processed in order. So there must not be any dependencies between different tasks.
As of Java 8, there would be no need to instantiate a separate ForkJoinPool, because a ForkJoinPool could also be called like this:
ForkJoinPool.commonPool().submit(...);
ForkJoinPool.commonPool().invoke(...);
new CountTask(2, N).invoke() //blocking
Modern asynchrony - CompletableFuture #
These run on forkjoinPool.commonPool()
and are invoked like this:
CompletableFuture<Long> future = CompletableFuture.supplyAsync(() -> longOperation());
process(future.get()); //join() throws only one unchecked exception
Continuation is possible as follows:
future.thenApply(result -> "My result: " + result); //result is "test".
future.thenAccept(result -> System.out.println(result)); //result is "My result: test", thenAccept(...) returns a Future<Void> and thenApply(...) returns a Future<Type>
There are other interesting functions such as:
CompletableFuture.allOf(future1, future2).thenAccept(...);
CompletableFuture.any(future1, future2).thenAccept(...);
Someone has to call future.join() or future.get() once, though, because otherwise the program might end and the ThreadPool threads might get killed. This happens because the threads in the pool are marked as daemon.