Saturday, October 31, 2009

Future




















Chapter 9 -
Concurrency Patterns
Patterns in Java, Volume 1: A Catalog of Reusable Design Patterns Illustrated with UML, Second Edition
by Mark Grand
John Wiley & Sons � 2002



























Future



This pattern is also known as Promise.




Synopsis


Use an object to encapsulate the result of a computation in a way that hides from its clients whether the computation is synchronous or asynchronous. The object will have a method to get the result of the computation. The method waits for the result if the computation is asynchronous and in progress or performs the computation if it is synchronous and not yet performed.






Context


Suppose you are designing a reusable class that will allow a program to get current weather data for a given location. A common type of application for this weather class is expected to be a display that includes weather information along with many other pieces of information. For example, the weather class might be used as part of a servlet that generates the HTML for a Web page that displays weather information to the side of some news.


The amount of time it will take to get weather information will vary greatly. It could be a fraction of a second or it could be many minutes. You want to design the weather class in a way that minimizes the impact on its clients.


You consider having instances of the weather class send an event to interested objects when it receives requested weather information. You decide against this. You are concerned that requiring the weather class’s clients to receive an asynchronous call that delivers an event notification forces too much complexity on them. A method to receive such a notification must arrange for the client to take notice of the new weather information in a thread-safe way.


Finally, you decide on the organization shown in Figure 9.37. Here are descriptions of the classes shown in Figure 9.37:






Figure 9.37: Weather information classes.


Weather.  Instances of this class encapsulate information about the current weather conditions at a particular location.



Client.  Instances of a class in this role use the information in Weather objects.



WeatherRequester.  When a Client object wants to get a Weather object that contains the current information for a given location, it asks a WeatherRequester object to request the information on its behalf. The Client object does this by calling the Weather Requester object’s getWeather method. The getWeather method does not wait for the weather information to be fetched. It returns immediately. The value it returns is a WeatherFuture object. The requested weather information is fetched asynchronously.



WeatherFuture.  This class is responsible for allowing other threads to coordinate their actions with a thread that is fetching weather information. When a WeatherRequester object’s getWeather method is called, it initiates the asynchronous fetching of requested weather information. It immediately returns a WeatherFuture object that is associated with the request.


A WeatherFuture object has a method named check that returns false if it is called before the requested information has been fetched and a Weather object containing the information has been created. After the Weather object has been created, calls to the check method return true.


A WeatherFuture object has another method named waitForWeather. If this method is called before the Weather object has been created, it waits until the Weather object has been created. Once the Weather object has been created, calls to the waitForWeather method immediately return the Weather object.






Forces

















J



A computation takes place asynchronously of other threads that use the result of the computation.




J



An asynchronous computation can notify objects that use its result that the result is available by using the Observer pattern or passing the object an event of some sort. For this to work properly, it may be necessary to have single-threaded access to some methods or sections of code. In some situations, this may add an unacceptable level of overhead or complexity to a class.




J



You want to encapsulate a computation so that its clients do not need to know whether it is synchronous or asynchronous.








Solution


Instead of designing a method to directly return the result of a computation, have it return an object that encapsulates the computation. The object that encapsulates the computation will have a method that can be called to get the result of the computation. Callers of this method do not know whether the computation is performed synchronously or asynchronously of their thread.



Figure 9.38 shows the roles that classes play in this solution. Here are descriptions of the roles that classes play in the Future pattern.






Figure 9.38: Future pattern.


Result.  Instances of a class in this role encapsulate the result of a computation.



Client.  Instances of classes in this role use Result objects. Client objects initiate the process of getting a Result object by passing the appropriate parameters to a Requester object’s doIt method.



Requester.  A class in this role has a method that can be called to initiate the computation of a Result object. In Figure 9.38, this method is indicated with the name doIt. The doIt method returns a Future object that encapsulates that computation.



Future.  Each instance of a class in this role encapsulates the computation of a Result object. The computation may be synchronous or asynchronous. If the computation is asynchronous, then it is computed in its own thread. An asynchronous computation may already be underway when the Future object that encapsulates it is created.



Future objects have a method that Client objects call to get the result of a computation. In Figure 9.38, this method is indicated with the name getResult. The way that this method works depends on whether the computation is synchronous or asynchronous.


If the computation is asynchronous and the method is called before the computation has completed, then the method waits until the computation is complete and then returns the result. If the method is called after the computation has completed, then the method immediately returns the result.


If the computation is synchronous, the first time the method is called the computation is synchronously performed and its result is returned. Subsequent calls to the method just return the result. Alternatively, the computation may be performed when the Future object is created.



Figure 9.39 is a collaboration diagram that shows the interactions between objects in the Future pattern. Here are descriptions of the interactions shown in Figure 9.39:






Figure 9.39: Future interactions.



  1. A Client object calls a Requester object’s doIt method. This initiates a computation that is encapsulated by the Future object that the doIt method returns.




  2. After spending some time doing other things, the Client object calls the Future object’s getResult method. The getResult method returns the result of the computation.








Implementation



Polling


A common usage pattern for the Future pattern is for the Client object to call a Requester object’s doIt method to start a computation before it needs the result. It then does some other things. When it finally needs the result of the computation, it calls the Future object’s getResult method. If the computation is asynchronous, initiating the computation as soon as possible and then getting its result as late as possible maximizes the advantages of the asynchronous processing.


In some situations this usage is not appropriate. One such situation is when the client has an ongoing task that it performs continuously. Consider the example of a servlet that serves a Web page containing news articles and weather information. Suppose this servlet uses the weather-related classes discussed under the Context heading. After it has requested weather information, there is no one point in what this servlet does that you can regard as the latest possible time to get the weather information. The servlet is continuously updating the information it uses for the content for the Web page it serves. You don’t want any request for the servlet to provide Web content to be delayed by having to wait for an update to weather information.


One way to resolve this problem would be to have a separate thread for each request for weather information. Using a thread for this purpose does not add much value and seems like a waste of resources.


A better solution is to add another method to the Future class. Typically, this method will have a name that begins with the word check, such as checkComputation. The purpose of this check method is to check whether a Client object will wait idly if it calls the Future object’s getResult method. A check method may be helpful if the encapsulated computation is asynchronous. The check method will return false if it is called before the computation has completed or true if it is called after the computation has completed. If the computation is synchronous, then the check method always returns true.


Having a check method allows Client objects to poll Future objects. Each request for content can poll a Future object by calling its check method. If the check method returns true, the servlet updates the weather information that is used for the Web page’s content. If the check method returns false, then the servlet continues to use the information that it already has.





Proxy


The Future pattern is sometimes used with the Proxy pattern. This is generally done by having the class in the Future role implement an interface that allows it to be used as a proxy to the class performing the computation. When used this way, the object that initiates the computation and creation of the Future object and the object that gets the result from the Future object are usually not the same object.


An object will initiate the creation of the Future object. It will pass the Future object to other objects that will access the object through an interface without any knowledge of the Future object’s class or its participation in the Future pattern. When the Future and Proxy patterns are combined in this way and the computation is synchronous, the result is the Virtual Proxy pattern.





Launching a Synchronous Computation


If a Future object encapsulates a synchronous computation, it must be able to launch the computation the first time that its getResult method is called. To launch the computation, the Future object must be able to provide the computation with its parameters. For this reason, if a Future class encapsulates a synchronous computation, the class’s constructor will take the necessary parameters to provide the parameter values to the computation.





Rendezvous


Implementing a Future class to work with an asynchronous computation involves getting the thread that calls its getResult method to wait until the computation is finished. There is a name for the technique of getting multiple threads to wait until one or more of them reach a certain point. The name of this technique is rendezvous.


The java.lang.Thread class has a method named join that provides a convenient way of implementing rendezvous. A Thread object’s join method does not return until the thread dies. If a thread will perform the asynchronous computation associated with a Future object and then die, then the Future object can call the thread’s join method to rendezvous with it.


A thread does not necessarily die when it finishes a computation. It may have something else to do. It may be under the management of a thread pool (the Thread Pool pattern is described in Patterns in Java, Volume 3) that will reuse the thread for another computation. To implement rendezvous in a way that does not assume what a thread will do after it finishes an asynchronous computation, you should use wait and notifyAll. The code listing of the AsynchronousFuture class under the Code Example heading shows an example of this technique.





Exceptions


If the computation associated with a Future object throws an exception, you want the exception to be thrown out of the Future object’s getResult method. If the computation is performed synchronously, then any exceptions that it throws will, as a natural consequence, be thrown out of the Future object’s getResult method.


If the computation is asynchronous and it throws an exception, the natural course of events is for it to throw the exception in the currently running thread. In order for the exception to be thrown out of the Future object’s getResult method, you will need to catch the exception and set one of the Future object’s instance variables to refer to it. You can then code the getResult method so that if the instance variable is not null, the getResult method throws the exception that it refers to.








Consequences














J



The classes that use a computation are relieved of any responsibility for concurrency.




J



If the computation is asynchronous, all of the complexity related to synchronization is in the Future class.








Java API Usage


The java.awt.MediaTracker class is an example of the Future pattern in the Java API. The MediaTracker class encapsulates the process of asynchronously loading image data into the ImageProducer object associated with an Image object. It fills both the Requester and the Future roles.


The MediaTracker class does not encapsulate the result of loading the image data. It just encapsulates the process of loading the data. The ImageProducer object fills the Result role.






Code Example


The code example for this pattern is based on the design for reusable classes to fetch current weather information that was discussed under the Context heading. The first class presented is the WeatherRequester class that fills the requester role.




public class WeatherRequester {
/**
* The object that does the real work of fetching weather
* information.
*/
    WeatherFetchIF fetcher;

    public WeatherRequester(WeatherFetchIF fetcher) {
        this.fetcher = fetcher;
    }  // constructor(WeatherFetchIF)

/**
* Initiate the process of getting current weather data for
* the given geographical coordinates.
*/
    public synchronized WeatherFuture getWeather(Coordinate location) {
        return new WeatherFuture(fetcher, location);
    }  // getWeather(Coordinate)
}  // class WeatherRequester


The constructor for the WeatherRequester class is passed the object that will do the real work of fetching current weather information for a given location. Its getWeather method is passed a location and creates a WeatherFuture object by passing the weather fetching object and the location to the WeatherFuture class’s constructor. The WeatherFuture class fills the Future role.




public class WeatherFuture {
/**
* When a coordinate is passed to the constructor, it puts
* the coordinate for the request in this instance variable
* so that it is visible to the new thread that it starts.
*/
    private Coordinate location;

/**
* The object to use to fetch current weather information.
*/
    private WeatherFetchIF fetcher ;

/**
* An object to provide support logic.
*/
    private AsynchronousFuture futureSupport ;

/**
* Construct a WeatherFuture object that encapsulates the
* fetching of weather information for the given coordinate
* using the given WeatherFetchIF object.
*/
    public WeatherFuture(WeatherFetchIF fetcher,
                         Coordinate location) {
        this.fetcher = fetcher;
        this.location = location;
        futureSupport = new AsynchronousFuture();
        new Runner().start();
    }  // constructor(WeatherFetchIF, Coordinate)

/**
* Return true if the requested weather info has been
* fetched.
*/
    public boolean check() {
        return futureSupport.checkResult();
    }  // checkResult()

/**
* Return the result object for this future. If it has not
* yet been set, wait until it is set.
*/
    public synchronized WeatherIF waitForWeather() throws Exception {
        return (WeatherIF)futureSupport.getResult();
    }  // getResult()

/**
* This private class provides the top-level logic for
* asynchronously fetching current weather information.
*/
    private class Runner extends Thread {
        public void run() {
            try {
                WeatherIF info = fetcher.fetchWeather(location);
                futureSupport.setResult(info);
            } catch (Exception e) {
                futureSupport.setException(e);
            }  // try
        }  // run()
    }  // class runner
}  // class WeatherFuture


When the weather information has been fetched, it is encapsulated in an instance of a class that implements an interface named WeatherIF. The core logic for managing the asynchronous computation is delegated to a reusable class named AsynchronousFuture.




public class AsynchronousFuture {
    private Object result;
    private boolean resultIsSet;
    private Exception problem;

/**
* Return true if the result has been set.
*/
    public boolean checkResult() {
        return resultIsSet;
} // checkResult()

/**
* Return the result object for this future. If it has not
* yet been set, wait until it is set.
*/
    public synchronized Object getResult() throws Exception {
        while (!resultIsSet) {
            wait();
} // while
        if (problem!=null) {
            throw problem;
} // if problem
        return result;
} // getResult()

/**
* Call this method to set the result of a computation.
* This method should only be called once.
*/
    public synchronized void setResult(Object result) {
        if (resultIsSet) {
            String msg = "Result is already set";
            throw new IllegalStateException(msg);
} // if
        this.result = result;
        resultIsSet = true;
        notifyAll();
} // setResult(Object)

/**
* If the asynchronous computation associated with this
* object throws an exception, pass the exception to this
* method and the exception will be rethrown by the
* getResult method.
*/
    public synchronized void setException(Exception e) {
        problem = e;
        resultIsSet = true;
        notifyAll();
} // setException(Exception)
} // class AsynchronousFuture






Related Patterns



Asynchronous Processing.  The Asynchronous Processing pattern provides guidance on when to make a computation asynchronous. It is often used with the Future pattern.



Observer.  Using the Observer pattern is an alternative to polling a Future object to determine whether its associated asynchronous computation has completed.



Proxy.  The Future pattern can be combined with the Proxy pattern so that the Future object is also a proxy for the object that performs that underlying computation.



Virtual Proxy.  If the Future pattern is combined with the Proxy pattern and the computation is synchronous, then what you have is equivalent to the Virtual Proxy pattern.



Active Object.  The Active Object pattern described in [SSRB00] describes a way of combining the Future pattern with the Asynchronous Processing pattern.


















No comments:

Post a Comment