Beliebte Suchanfragen
|
//

Executing tasks in parallel using Java Future

13.10.2011 | 5 minutes of reading time

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:

1import java.util.ArrayList;
2package de.codecentric.blog.sample;
3 
4import java.util.ArrayList;
5import java.util.Calendar;
6import java.util.List;
7import java.util.concurrent.Callable;
8import java.util.concurrent.ExecutionException;
9import java.util.concurrent.ExecutorService;
10import java.util.concurrent.Executors;
11import java.util.concurrent.FutureTask;
12 
13public class FutureTaskDemo {
14 
15    /**
16     * Maximum amount of numbers to check
17     */
18    public static final int MAX_NUMBER = 2000000000;
19 
20    /**
21     * Returns the amount of numbers that can be divided by the divisor without remainder.
22     * @param first First number to check
23     * @param last Last number to check
24     * @param divisor Divisor
25     * @return Amount of numbers that can be divided by the divisor without remainder
26     */
27    public static int amountOfDivisibleBy(int first, int last, int divisor) {
28 
29        int amount = 0;
30        for (int i = first; i <= last; i++) {
31            if (i % divisor == 0) {
32                amount++;
33            }
34        }
35        return amount;
36    }
37 
38    /**
39     * Returns the amount of numbers that can be divided by the divisor without remainder (using parallel execution).
40     * @param first First number to check
41     * @param last Last number to check
42     * @param divisor Divisor
43     * @return Amount of numbers that can be divided by the divisor without remainder
44     * @throws InterruptedException
45     * @throws ExecutionException
46     */
47    public static int amountOfDivisibleByFuture(final int first, final int last, final int divisor)
48            throws InterruptedException, ExecutionException {
49 
50        int amount = 0;
51 
52        // Prepare to execute and store the Futures
53        int threadNum = 2;
54        ExecutorService executor = Executors.newFixedThreadPool(threadNum);
55        List<FutureTask<Integer>> taskList = new ArrayList<FutureTask<Integer>>();
56 
57        // Start thread for the first half of the numbers
58        FutureTask<Integer> futureTask_1 = new FutureTask<Integer>(new Callable<Integer>() {
59            @Override
60            public Integer call() {
61                return FutureTaskDemo.amountOfDivisibleBy(first, last / 2, divisor);
62            }
63        });
64        taskList.add(futureTask_1);
65        executor.execute(futureTask_1);
66 
67        // Start thread for the second half of the numbers
68        FutureTask<Integer> futureTask_2 = new FutureTask<Integer>(new Callable<Integer>() {
69            @Override
70            public Integer call() {
71                return FutureTaskDemo.amountOfDivisibleBy(last / 2 + 1, last, divisor);
72            }
73        });
74        taskList.add(futureTask_2);
75        executor.execute(futureTask_2);
76 
77        // Wait until all results are available and combine them at the same time
78        for (int j = 0; j < threadNum; j++) {
79            FutureTask<Integer> futureTask = taskList.get(j);
80            amount += futureTask.get();
81        }
82        executor.shutdown();
83 
84        return amount;
85    }
86 
87    /**
88     * Executing the example.
89     * @param args Command line arguments
90     * @throws ExecutionException
91     * @throws InterruptedException
92     */
93    public static void main(String[] args) throws InterruptedException, ExecutionException {
94 
95        // Sequential execution
96        long timeStart = Calendar.getInstance().getTimeInMillis();
97        int result = FutureTaskDemo.amountOfDivisibleBy(0, MAX_NUMBER, 3);
98        long timeEnd = Calendar.getInstance().getTimeInMillis();
99        long timeNeeded = timeEnd - timeStart;
100        System.out.println("Result         : " + result + " calculated in " + timeNeeded + " ms");
101 
102        // Parallel execution
103        long timeStartFuture = Calendar.getInstance().getTimeInMillis();
104        int resultFuture = FutureTaskDemo.amountOfDivisibleByFuture(0, MAX_NUMBER, 3);
105        long timeEndFuture = Calendar.getInstance().getTimeInMillis();
106        long timeNeededFuture = timeEndFuture - timeStartFuture;
107        System.out.println("Result (Future): " + resultFuture + " calculated in " + timeNeededFuture + " ms");
108    }t
109}

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:

1int threadNum = 2;
2        ExecutorService executor = Executors.newFixedThreadPool(threadNum);
3        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.

1FutureTask<Integer> futureTask_1 = new FutureTask<Integer>(new Callable<Integer>() {
2            @Override
3            public Integer call() {
4                return FutureTaskDemo.amountOfDivisibleBy(first, last / 2, divisor);
5            }
6        });
7        taskList.add(futureTask_1);
8        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.

1for (int j = 0; j < threadNum; j++) {
2            FutureTask<Integer> futureTask = taskList.get(j);
3            amount += futureTask.get();
4        }
5        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.

|

share post

//

Gemeinsam bessere Projekte umsetzen.

Wir helfen deinem Unternehmen.

Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.

Hilf uns, noch besser zu werden.

Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.