Saturday, April 02, 2005

Futures and Eventual Values Part 3

Part 3 of my blogging on Futures and Eventual Values

In this part I discuss combining Futures with the Proxy Pattern. I will not discuss the differences between Futures and Eventual Values any further (unless provoked!), and indeed where ever I refer to Futures from now on applies equally to Eventual Values.

A big disadvantage with using futures as I have shown them so far is that the impact on the client is significant. Instead of returning a simple float, the getTemperature() method of my weather service is now returning a Future<Float>. This is unfortunate if I want the freedom to switch back and forth between the synchronous version and asynchronous version. It is also a bit ugly to be exposing the fact that we are dealing with futures deep down in the presentation logic (i.e. where I am printing the results).

One solution is to use the Proxy Pattern. Unfortunately, you can not create proxies for primitives like float. In Java, because Float does not implement an interface, you can not even create a proxy for a Float. Turning a problem into an opportunity, a good developer would say that getTemperature() should not be returning raw types anyway. Perhaps it should be returning a Temperature object with some useful methods (e.g. temperature conversion to/from celcius and fahrenheit).

Here is a suggestion for a Temperature interface:


public interface Temperature {
double getCelciusValue();
double getFahrenheitValue();
}


Similarly, our weather service should be implementing an interface:


public interface WeatherService {
Temperature getTemperature();
}


Now our client code looks like this (using a synchronous version of the weather service):


WeatherService melb = new RemoteWeatherService("Melbourne");
WeatherService sydney = new RemoteWeatherService("Sydney");
WeatherService brisbane = new RemoteWeatherService("Brisbane");
Temperature melbTemperature = melb.getTemperature();
Temperature sydneyTemperature = sydney.getTemperature();
Temperature brisbaneTemperature = brisbane.getTemperature();
System.out.println("City\t\tTemperature");
System.out.println("Melbourne\t" + melbTemperature.getCelciusValue());
System.out.println("Sydney\t\t" + sydneyTemperature.getCelciusValue());
System.out.println("Brisbane\t" + brisbaneTemperature.getCelciusValue());


It is now possible to develop an asynchronous version of the weather service that only requires the client to change the first three lines to use AsynchWeatherService instead of RemoteWeatherService to look like this:


// NOTE: Specifying a 10 second time out. This is discussed later.
WeatherService melb = new AsynchWeatherService("Melbourne", 10, TimeUnit.SECONDS);
WeatherService sydney = new AsynchWeatherService("Sydney", 10, TimeUnit.SECONDS);
WeatherService brisbane = new AsynchWeatherService("Brisbane", 10, TimeUnit.SECONDS);
Temperature melbTemperature = melb.getTemperature();
Temperature sydneyTemperature = sydney.getTemperature();
Temperature brisbaneTemperature = brisbane.getTemperature();
System.out.println("City\t\tTemperature");
System.out.println("Melbourne\t" + melbTemperature.getCelciusValue());
System.out.println("Sydney\t\t" + sydneyTemperature.getCelciusValue());
System.out.println("Brisbane\t" + brisbaneTemperature.getCelciusValue());


The key to this is that AsynchWeatherService is a proxy for RemoteWeatherService (both implementing WeatherService), and it returns a proxy for the Temperature object returned by RemoteWeatherService. The twist is that the Temperature proxy actually takes an object of type Future<Temperature>. First, let's look at AsynchWeatherService:


public class AsynchWeatherService implements WeatherService {
private RemoteWeatherService weatherStation;
private final long timeout;
private final TimeUnit timeoutUnit;

public AsynchWeatherService(String city, long timeout, TimeUnit timeoutUnit) {
this.weatherStation = new RemoteWeatherService(city);
this.timeout = timeout;
this.timeoutUnit = timeoutUnit;
}

public Temperature getTemperature() {
FutureTask<Temperature> result = new FutureTask<Temperature>(
new Callable<Temperature>() {
public Temperature call() {
return weatherStation.getTemperature();
}
});
new Thread(result).start();
return new FutureTemperature(result, timeout, timeoutUnit);
}

}


As before, the getTemperature() method creates a FutureTask object initialized with a Callable that simply invokes the getTemperature() method of the RemoteWeatherStation. It then hands the FutureTask to a Thread object and starts the new thread. Finally, it creates and returns a FutureTemperature object, where FutureTemperature is the Temperature proxy I mentioned (or could be thought of as an Adapter, as it adapts Future<Temperature> to look like a Temperature). It is implemented as follows:


public final class FutureTemperature implements Temperature {
private final Future<Temperature> futureTemperature;
private final long timeout;
private final TimeUnit timeoutUnit;

public FutureTemperature(Future<Temperature> futureTemperature, long timeout, TimeUnit timeoutUnit) {
this.futureTemperature = futureTemperature;
this.timeout = timeout;
this.timeoutUnit = timeoutUnit;
}

public double getCelciusValue() {
return getFutureTemperature().getCelciusValue();
}

public double getFahrenheitValue() {
return getFutureTemperature().getFahrenheitValue();
}

private Temperature getFutureTemperature() {
// TODO: Should give more thought to exceptions!
try {
return futureTemperature.get(timeout, timeoutUnit);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
} catch (TimeoutException e) {
throw new RuntimeException(e);
}
}

}


I suppose it is quite a bit of work, but the end result is that the fact that futures are being used is now hidden from the client (other than the configuration of the weather services). Notice that in this example I allow for the specification of a timeout. This is important or otherwise the client thread may wait forever if the remote service fails. One drawback with using proxies as above is that the client can not specify the timeout at the point where they request the value, but instead have to specify it as part of the service configuration. For example, the client cannot pass a timeout to the getCelciusValue() method of the Temperature object since the Temperature interface quite naturally does not expect a timeout. In practice I suspect this is a small price to pay.

As hinted in my last blog, it is easy to fall into the trap of using futures when other concurrency patterns are more appropriate. Note that in my example above, by the time the main thread has invoked the getCelciusValue() method of the melb temperature object, you will end up with three threads all blocked waiting for responses from the three weather services, and the main thread blocked waiting on the melbourne temperature future. If this is a simple desktop client connecting to three different weather services then that is fine. But if you had to connect to thousands of weather services, or if this were part of a server side application that was expected to have a high transaction rate, then the result can be a large number of threads all sitting around doing effectively nothing. Threads can be expensive (they are usually an operating system level resource).

Often a more efficient approach is one based on message queues. Use of message queues could reduce the thread consumption to a single thread sending one-way messages to all of the remote services, and then fetching their responses off of a single in-coming message queue. But now I have gone completely off topic and so will stop for now!

2 Comments:

Blogger AK said...

when a FutureTask computation is cancelled, does that release all the resources (DB connections etc.) held by the computing thread i.e. the callable object?

7:17 AM  
Blogger David Kemp said...

Interesting question. I had to peek at the source code of FutureTask, and it looks like cancel() invokes interrupt() on the associated Thread. I believe that, so long as you explicitly release your resources inside 'finally' blocks, then you can be sure they will be released. It is worth experimenting with this, and having a read of the Java API on Thread.interrupt(). I am surprised to see a comment more than two years after I posted the blog.

9:20 PM  

Post a Comment

<< Home