Execute the Future
No matter how much multithreaded code you write, it will never be simple. The added complexity of races between threads makes it much harder to predict the possible combinations of events in your program. Even worse, testing the combinations is difficult, requiring thread-aware test cases with explicit synchronisation points. Thus it is a pleasure to see a package like java.util.concurrent offering more than just synchronisation primitives, but also higher level abstractions1.
One of the simplest but most useful abstractions in the package is the
abstracts the result of an asynchronous computation
By encapsulating an operation in a Future, you can execute it asychronously (typically using an Executor). The Future may be queried, cancelled, and most importantly can return the result of the computation. When asking for the result (via one of the get methods), you can also specify a timeout indicating how long you are willing to wait for the result to become available.
To take a concrete example, recently I had a problem with some networking code. The purpose of the code was to ping a bunch of machines to see which of them were online and responding. Unfortunately, when a machine is unavailable it can take some time for the ping to fail, making the whole process take several minutes. In my case, such a long ping time indicates that the machine is essentially unusable anyhow, so I would prefer to fail the ping after a reasonable delay, say 10 seconds. I achieved this timeout without a single line of sychronisation code by using the FutureTask implementation of the Future interface:
{
private ExecutorService pingService =
Executors.newCachedThreadPool();
private class Pinger implements
Callable<SlaveStatus>
{
private SlaveAgent agent;
public Pinger(SlaveAgent agent)
{
this.agent = agent;
}
public SlaveStatus call() throws Exception
{
SlaveStatus status;
try
{
agent.getSlaveService().ping();
}
catch (Exception e)
{
status = new SlaveStatus(Status.OFFLINE,
e.getMessage());
}
return status;
}
}
void pingSlave(SlaveAgent agent)
{
FutureTask<SlaveStatus> future =
new FutureTask<SlaveStatus>(new Pinger(agent));
pingService.execute(future);
SlaveStatus status;
try
{
status = future.get(10, TimeUnit.SECONDS);
}
catch (TimeoutException e)
{
status = new SlaveStatus(Status.OFFLINE,
“Agent ping timed out”);
}
catch(Exception e)
{
status = new SlaveStatus(Status.OFFLINE,
e.getMessage());
}
agent.updateStatus(status);
}
}
Although I have simplified the code for clarity2, the most error-prone part of the implementation all lives in java.util.concurrent, which is tried and tested. There was no need for me to carefully synchronise and test for races, I was able to just get on with the job.
This is just one small part of the functionality offered by Futures. Combining them with CompletionServices, for example, allows an even higher level of abstraction for dealing with multiple tasks. If you write multithreaded code, and haven’t looked at the java.util.concurrent package, then do yourself a favour and dive in. The JavaDoc comes complete with usage suggestions and example code snippets, making the package very approachable. Package designer Doug Lea has also authored multiple books on the topic.
—
1 moving out of the imperative world we get into even more interesting abstractions to help exploit concurrency, but that’s a topic all of its own.
2 the most significant omission is how to handle the tasks that did not complete before the timeout; in some applications they may be a resource burden, in others it may be safe to let them complete in their own time









July 20th, 2006 at 11:25 am
Have you considered the “futures” derived work that Doug Lea, BEA, IBM and Tangosol (among others on the expert group) have put into JSR 236/237?
July 20th, 2006 at 12:28 pm
Cameron,
I am not familiar with the JSRs, but had a quick look at the JavaOne presentation and it looks promising. Thanks for the tip.