Java Concurrency Part 4 – Callable, Future

One of the beautiful things about Java from its very first release was the ease with which we could write multi-threaded programs and introduce asynchronous processing into our designs. The Thread class and Runnable interface combined with Java’s memory management model meant for straightforward thread programming. But as discussed in Part 3, neither the Thread class nor the Runnable interface allowed for thrown Exceptions or returned values. The lack of returned values was mildly annoying.

The lack of thrown checked exceptions was a little more serious. The contract was public void run() which meant you had to catch checked exceptions and do something with them. Even if you were careful and you stored these for later verification, you couldn’t force all uses of the class to check the exception. You could go through all your getters and throw the Exception if it existed on each one. Besides being cumbersome, even that wasn’t foolproof. You couldn’t enforce calls to any of these. Thread programmers would correctly call join() to wait for it complete and may then have gone on their merry way.

Not to worry though, after many years, this was finally addressed in the 1.5 release. With the introduction of the Callable and Future interfaces and their support in the thread pools discussed in our last post, both of these issues have been addressed quite elegantly.

Callable
The Callable interface declares public T call() throws Exception. Now we can return a result, have it strongly typed as declared in our implementation and even throw Exceptions. While there are some utility methods in the Executors class to convert your Runnable instances as discussed in Part 3, you would do well to review your current implementations of Runnable or subclasses of Thread. Why bother? Primarily to double check and remove the workaround you may have implemented to address the lack of support for thrown Exceptions. At the same time, you may wish to make use of the ability to return results right in the execution method eliminating any need to cast to retrieve values.

Future
Here’s where the combined power of the thread pools and Callable come together. Future is another new interface introduced in 1.5. When you submit a Callable to one of the thread pools, you are provided an instance of Future that is typed to the Callable you passed in. This object substitutes for an actual Thread instance that you would have used prior to 1.5. Whereas you previously had to do Thread.join() or Thread.join(long millis), now you may use them as in this example.

public class ServerAcceptingRequestsVerifier implements Callable {
	/**
	 * @return Boolean.TRUE is server is accepting requests
	 * Boolean.FALSE otherwise
	 */
	public Boolean call() throws Exception {
		Boolean isAcceptingRequests = null;
		... ask server about taking requests here
		return isAcceptingRequests;
	}
}
public Boolean isServerTakingRequests(String server) 
			throws UnresponsiveException, InterruptedException {
	ServerAcceptingRequestsVerifier acceptingRequestsVerifier = 
		new ServerAcceptingRequestsVerifier();
	Future future = 
		THREAD_POOL.submit(acceptingRequestsVerifier);
	try {
		Boolean isAcceptingRequests = future.get();
		//waits for the thread to complete, even if it hasn't started
		return isAcceptingRequests;
	} catch (ExecutionException e) {
		throw new UnresponsiveException(e.getCause());
	}

}

It’s also nice that we now have explicit TimeoutException if we decide to limit how long we’re willing to wait for completion.

try {
	Boolean isAcceptingRequests = future.get(5, TimeUnit.SECONDS);
	//this waits for 5 seconds, throwing TimeoutException if not done
	return isAcceptingRequests;
} catch (TimeoutException e) {
	LOGGER.warn("Timed out waiting for server check thread." +
		"We'll try to interrupt it.");
	future.cancel(true);
	return Boolean.FALSE;
} catch (ExecutionException e) {
	throw new UnresponsiveException(e.getCause());
}

In our next post, we’ll get into some of the new interfaces/classes that are used to make the thread pools work that are available for our use too.

Java Concurrency Part 3 – Thread Pools

One of the most generally useful concurrency enhancements delivered in Java 1.5 was the introduction of customizable thread pools. These thread pools give you quite a bit of control over things such as number of threads, reuse of threads, scheduling and thread construction. Let’s review these.

First, thread pools. Let’s dive right into java.util.concurrent.ExecutorService, which provides us the basic interface for a thread pool. All thread pools allow submitting Callable or Runnable instances for future execution. They also provide various pool management methods.

Pool Management

Various management methods exist for the pools. You can shutdown() the pool, which will reject any future submissions but complete processing of in-process executions and even those that had not yet started but were submitted before the shutdown was initiated. You can also more aggressively perform a shutdownNow(). This will also prevent any future submissions, but it has a few different, notable behaviours. It will not start execution of submitted but unstarted tasks. They will be in the returned list. It will also attempt to stop, or more precisely, Thread.interrupt() currently executing tasks. This is a best effort with no guarantee that these tasks will be successfully interrupted.

ThreadFactory

In a moment we will get into the java.util.concurrent.Executors builder class which can create various thread pool configurations, but first let’s focus for a second on using ThreadFactory. You’ll want to take advantage of ThreadFactory support in Executors and be in the habit of providing your own. The default ThreadFactory will give you give you an incrementing numbered pool naming scheme which is not all that helpful in logs or other monitoring. For the first pool created you’ll get threads named pool-1-thread-1, pool-1-thread-2 and the second one starts with pool-2-thread-1, etc. By providing your own ThreadFactory, you can have threads named like ReportProcessingThread1 and HttpThread1. Here’s a simple example:

private AtomicLong counter = new AtomicLong();
private String name;
public Thread newThread(Runnable r) {
	Thread t = new Thread(r);
	t.setName(name + counter.incrementAndGet());
	return t;
}

ThreadFactory will only be called when a new Thread is created. Given that the JDK thread pools will reuse threads whenever possible, this class cannot be used to manage the beginning of execution.

Executors Builder Methods

Now back to the Executors utility builder methods. They are:

  • newCachedThreadPool() will give you a thread pool that will reuse threads when possible, creating new ones as needed with no configured limit.
  • newFixedThreadPool(int nThreads) will give you a thread pool that will use only up to the number of threads specified but will accept as many tasks as submitted for execution running them in submission order.
  • newScheduledThreadPool(int corePoolSize) is used specifically for scheduling threads with delayed execution, on a recurring schedule on with recurring delay. The returned thread pool implements ScheduledExecutorService which exposes the additional scheduling methods schedule(Runnable command, long delay, TimeUnit unit), scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) and scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit).
  • newSingleThreadExecutor() and newSingleThreadScheduledExecutor(). These impose no limit on the number of tasks that can be submitted, only ensuring that a single thread/task is executing at a time.

Finally, there are a few helper methods for creating Callable instances from Runnable. This gets us into the newly created constructs for allowing threads to throw Exceptions and return values, something we had to work around quite painfully before. We’ll consider these and how they are used with these thread pools in our next post.