Java Reference
In-Depth Information
In all these cases, the usage is the same: you pass an instance of the
Callable
interface into the
executor, and it returns a
Future
instance that is a handle on the result. That
Future
instance also provides
access to any error that occurred during the processing. This usage has three major advantages over
the
Runnable
approach done by threads: first, you can return a value from your concurrent processing;
second, your errors are not silently swallowed (assuming you get the result from the
Future
); and third, a
thread can only run one
Runnable
instance, but an executor's thread can run handle multiple
Callable
instances. Just like with the
Thread
's inline
Runnable
implementation, though, we can provide our
Callable
implementation inline using lambdas. This makes the code much simpler to read.
We can see these advantages in play when we rewrite our kata using the executors. Instead of coming
up with a clever way to divvy up the work evenly, we will simply add all the work to be done into our primary
processing executor. Just like before, that executor will have as many threads as we have processors, and it
will be responsible for the CPU-intensive work. We will hand the I/O intensive work off to another executor,
which will be a single thread. Since the hand-off is cleaner, we can set the I/O intensive thread to be
maximum priority, ensuring that it executes as much as possible: we see again there that lambdas make the
code much more readable, since we can specify the
ThreadFactory
implementation inline. The code for this
kata is given in Listing 6-3.
Listing 6-3.
Using Lambdas to Define ExecutorService Work Instances
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.*;
public class Listing3 {
public static void printPrimes(int maxBitLength) {
ExecutorService executor =
Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
ExecutorService printExecutor = Executors.newSingleThreadExecutor(r -> {
Thread t = new Thread(r);
t.setPriority(Thread.MAX_PRIORITY);
return t;
}
);
// The resulting work
List<Future<?>> futures = new ArrayList<>(maxBitLength);
for (int i = 0; i < maxBitLength; i++) {
int bitLength = i + 1;
Future<String> stringFuture = executor.submit(() -> {
BigInteger prime = PrimeFactory.ofLength(bitLength);
return prime.toString();
}
);