Overview

Executing tasks in parallel using Java Future

1 Comment

Recently I was attending a really interesting presentation by Adam Bien who was using Future as part of his demo application. Future offers a very elegant way to implement parallel execution of tasks in Java. As I found the concept quite interesting I thought I give it a try in some pure sample class. And as I did so I thought I could as well write a short blog post at the same time, so here we go.

This is technical stuff, so let’s start with the sample class right away:

import java.util.ArrayList;
package de.codecentric.blog.sample;
 
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
 
public class FutureTaskDemo {
 
    /**
     * Maximum amount of numbers to check
     */
    public static final int MAX_NUMBER = 2000000000;
 
    /**
     * Returns the amount of numbers that can be divided by the divisor without remainder.
     * @param first First number to check
     * @param last Last number to check
     * @param divisor Divisor
     * @return Amount of numbers that can be divided by the divisor without remainder
     */
    public static int amountOfDivisibleBy(int first, int last, int divisor) {
 
        int amount = 0;
        for (int i = first; i <= last; i++) {
            if (i % divisor == 0) {
                amount++;
            }
        }
        return amount;
    }
 
    /**
     * Returns the amount of numbers that can be divided by the divisor without remainder (using parallel execution).
     * @param first First number to check
     * @param last Last number to check
     * @param divisor Divisor
     * @return Amount of numbers that can be divided by the divisor without remainder
     * @throws InterruptedException
     * @throws ExecutionException
     */
    public static int amountOfDivisibleByFuture(final int first, final int last, final int divisor)
            throws InterruptedException, ExecutionException {
 
        int amount = 0;
 
        // Prepare to execute and store the Futures
        int threadNum = 2;
        ExecutorService executor = Executors.newFixedThreadPool(threadNum);
        List<FutureTask<Integer>> taskList = new ArrayList<FutureTask<Integer>>();
 
        // Start thread for the first half of the numbers
        FutureTask<Integer> futureTask_1 = new FutureTask<Integer>(new Callable<Integer>() {
            @Override
            public Integer call() {
                return FutureTaskDemo.amountOfDivisibleBy(first, last / 2, divisor);
            }
        });
        taskList.add(futureTask_1);
        executor.execute(futureTask_1);
 
        // Start thread for the second half of the numbers
        FutureTask<Integer> futureTask_2 = new FutureTask<Integer>(new Callable<Integer>() {
            @Override
            public Integer call() {
                return FutureTaskDemo.amountOfDivisibleBy(last / 2 + 1, last, divisor);
            }
        });
        taskList.add(futureTask_2);
        executor.execute(futureTask_2);
 
        // Wait until all results are available and combine them at the same time
        for (int j = 0; j < threadNum; j++) {
            FutureTask<Integer> futureTask = taskList.get(j);
            amount += futureTask.get();
        }
        executor.shutdown();
 
        return amount;
    }
 
    /**
     * Executing the example.
     * @param args Command line arguments
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException, ExecutionException {
 
        // Sequential execution
        long timeStart = Calendar.getInstance().getTimeInMillis();
        int result = FutureTaskDemo.amountOfDivisibleBy(0, MAX_NUMBER, 3);
        long timeEnd = Calendar.getInstance().getTimeInMillis();
        long timeNeeded = timeEnd - timeStart;
        System.out.println("Result         : " + result + " calculated in " + timeNeeded + " ms");
 
        // Parallel execution
        long timeStartFuture = Calendar.getInstance().getTimeInMillis();
        int resultFuture = FutureTaskDemo.amountOfDivisibleByFuture(0, MAX_NUMBER, 3);
        long timeEndFuture = Calendar.getInstance().getTimeInMillis();
        long timeNeededFuture = timeEndFuture - timeStartFuture;
        System.out.println("Result (Future): " + resultFuture + " calculated in " + timeNeededFuture + " ms");
    }	
}

The algorithm (not sure I should call it an algorithm ;)) implemented here is checking how many numbers in a given range can be divided by a certain divisor without remainder. Please keep in mind that this is just a stupid example you could as well calculate something here that tells us why 42 is the Answer to the Ultimate Question of Life, the Universe, and Everything. I personally thought I stick to the more trivial problems for the time being.

The main-method is just used to call the two different methods calculating this number and taking the time needed for execution. The first method amountOfDivisibleBy is trivial and does not need any further explanation here. The second method amountOfDivisibleByFuture is where things are getting interesting.

First of all we are getting ourselves an Executor here that is used to start the Future-tasks later on, as well as a list in which we will be storing these tasks:

        int threadNum = 2;
        ExecutorService executor = Executors.newFixedThreadPool(threadNum);
        List<FutureTask<Integer>> taskList = new ArrayList<FutureTask<Integer>>();

For the sake of keeping this example simple then two Future-instances are created exactly the same way, added to the list and executed usind the Executor. And this is the nice thing about this. I now have an object (to be precise two objects) that represents the execution of my algorithm.

        FutureTask<Integer> futureTask_1 = new FutureTask<Integer>(new Callable<Integer>() {
            @Override
            public Integer call() {
                return FutureTaskDemo.amountOfDivisibleBy(first, last / 2, divisor);
            }
        });
        taskList.add(futureTask_1);
        executor.execute(futureTask_1);

On these objects I have now different ways on checking if the processing has finished already. In this example it makes sense to go for the “blocking call” using “get” on my Future-objects in a loop. This will only return once the processing is finished, thus in this example the first call will probably wait longer and when I reach the second object processing will be done already and the result is returned. Then the results are simply aggregated and returned at the end of the method.

        for (int j = 0; j < threadNum; j++) {
            FutureTask<Integer> futureTask = taskList.get(j);
            amount += futureTask.get();
        }
        executor.shutdown();

Indeed it can be seen that execution time of the method using Future is almost twice as fast as the purely sequential execution.

Result         : 666666667 calculated in 12500 ms
Result (Future): 666666667 calculated in 6922 ms

As often in these kind of articles this is of course a somewhat artificial example, but I hope it shows quite well how nicely Future can be used to execute tasks in parallel in Java. And maybe there is some real life example of parallel processing just around the next corner sitting there and waiting to get solved using this.

Thomas Jaspers

Long-term experience in agile software projects using
Java enterprise technologies. Interested in test automation tools and concepts.

Share on FacebookGoogle+Share on LinkedInTweet about this on TwitterShare on RedditDigg thisShare on StumbleUpon

Kommentare

Comment

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