Java: FutureTask – Explained

Java FutureTasks are a great means of offloading a computation so that you can worry about it later, but they can be tricky to use. In this post we’ll dive into a few examples to explain the utility of this java feature.

A bit on Callable

Before understanding a FutureTask, we must first understand a Callable. Per the Javadocs, a Callable is defined as:

A task that returns a result and may throw an exception. Implementors define a single method with no arguments called call.

The important thing to note here is that a Callable is a computation that returns a result. For this exercise we’re going to define a method that returns a Callable whose result will be of type Integer

public static Callable<Integer> generateCallable() {
  return  new Callable<Integer>() {
    public Integer call() {
      Random random = new Random();
      int result = 0;
      //wasting clock cycles here to simulate a long running computation
      for (int i = 0; i < 1000000000; i++) {
        result = random.nextInt();
      }
      return result;
    }
  };
}

So now we have a Callable that we can use and get an Integer back. An example usage would be

 public static void useCallable() {
        Callable<Integer> c = generateCallable();
        try {
            Integer myValue = c.call();
            System.out.println("The value from the callable is " + myValue);
        }
        catch(Exception e) {

        }
    }

Using FutureTask

The problem with using a Callable directly is that the code invoking call has to block. This will cause the rest of the application to wait which can become a problem if several of these call invocations are required. A FutureTask can help alleviate the issue.

Using a FutureTask involves 3 steps:

  1. Create one using the constructor that takes a Callable
  2. Begin the computation by calling run
  3. Call get on the object once you’re ready for the result
Callable<Integer> callable1 = generateCallable();
FutureTask<Integer> task1 = new FutureTask<Integer>(callable1);
task1.run();
try {
  int result = task1.get();
  System.out.println("Result1 is " + result);
}
catch(ExecutionException e) {
  System.out.println(e.getMessage() );
  e.printStackTrace();
}
catch(InterruptedException ie) {
  System.out.println(ie.getMessage() + ie.getCause());
}      

A Future with Problems

The code in our previous example has some problems. We can exacerbate our problem by running 2 tasks ‘at the same time’. If you run the code below you will realize that the debug message is not printed until the calls to run have completed.

public static void doInMainThread() {
        Callable<Integer> callable1 = generateCallable();
        Callable<Integer> callable2 = generateCallable();

        FutureTask<Integer> task1 = new FutureTask<Integer>(callable1);
        FutureTask<Integer> task2 = new FutureTask<Integer>(callable2);
        task1.run();
        task2.run();

        System.out.println("Called run method"); //This isn't printed until the calls to run() have completed
        try {
            int result = task1.get();
            int result2 = task2.get();
            System.out.println("Result1 is " + result);
            System.out.println("Result2 is " + result2);
        }
        catch(ExecutionException e) {
            System.out.println(e.getMessage() );
            e.printStackTrace();
        }
        catch(InterruptedException ie) {
            System.out.println(ie.getMessage() + ie.getCause());
        }
    }

This is because FutureTasks do not run in a separate thread by default!

Thread to the Rescue

To solve our problem we can use a Thread. (We could also use an ExecutorService to fix this problem but we will get into that in another post) Creating a FutureTask now involves the following 4 steps:

  1. Create a FutureTask using the constructor that takes a Callable
  2. Create a Thread that takes the FutureTask as a param
  3. Begin the computation by calling start()
  4. Call get on the FutureTask once you’re ready for the result

Let’s rewrite our example with our newfound knowledge:

        Callable<Integer> callable1 = generateCallable();
        Callable<Integer> callable2 = generateCallable();

        FutureTask<Integer> task1 = new FutureTask<Integer>(callable1);
        FutureTask<Integer> task2 = new FutureTask<Integer>(callable2);
        Thread t1 = new Thread(task1);
        Thread t2 = new Thread(task2);
        t1.start();
        t2.start();
        System.out.println("Called run method"); //This is printed immediately
        try {
            int result = task1.get();
            int result2 = task2.get();
            System.out.println("Result1 is " + result);
            System.out.println("Result2 is " + result2);
        }
        catch(ExecutionException e) {
            System.out.println(e.getMessage());
            e.printStackTrace();
        }
        catch(InterruptedException ie) {
            System.out.println(ie.getMessage() + ie.getCause());
        }

By running this we see that our debug message is printed immediately and does not wait on run to complete.

FutureTasks are a useful construct that can be used anywhere that requires offloading computation. Use them to your advantage.

Post navigation


Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>