Getting a result from a parallel task in Java

In this post we saw how to execute a task on a different thread in Java. The examples demonstrated how to start a thread in the background without the main thread waiting for a result. This strategy is called fire-and-forget and is ideal in cases where the task has no return value.

However, that’s not always the case. What if we want to wait for the task to finish and return a result? Welcome to the future… or to the Future with a capital F.

Imagine that we have the following interface to implement various heavy calculations:

public interface CalculationService
{
    public int calculate(int first, int second);
}

…and we have the following implementations:

public class AdditionService implements CalculationService
{

    @Override
    public int calculate(int first, int second)
    {
        try
        {
            Thread.sleep(1000);
        } catch (InterruptedException ex)
        {
            //ignore
        }
        return first + second;
    }
    
}

public class SubtractionService implements CalculationService
{

    @Override
    public int calculate(int first, int second)
    {
        try
        {
            Thread.sleep(2000);
        } catch (InterruptedException ex)
        {
            //ignore
        }
        return first - second;
    }    
}

public class MultiplicationService implements CalculationService
{
    @Override
    public int calculate(int first, int second)
    {
        try
        {
            Thread.sleep(3000);
        } catch (InterruptedException ex)
        {
            //ignore
        }
        return first * second;
    }
}

public class DivisionService implements CalculationService
{
    @Override
    public int calculate(int first, int second)
    {
        try
        {
            Thread.sleep(4000);
        } catch (InterruptedException ex)
        {
            //ignore
        }
        return first / second;
    }
}

The sleeping threads are meant to simulate that a long running calculation produces the results.

If we want to get the result from all 4 services then we can obviously call them one by one at first:

CalculationService adder = new AdditionService();
CalculationService subtractor = new SubtractionService();
CalculationService multiplier = new MultiplicationService();
CalculationService divider = new DivisionService();
int firstOperand = 10;
int secondOperand = 5;

//all on single thread
Instant start = Instant.now();
int addResult = adder.calculate(firstOperand, secondOperand);
int subtractResult = subtractor.calculate(addResult, secondOperand);
int multplResult = multiplier.calculate(addResult, secondOperand);
int divResult = divider.calculate(addResult, secondOperand);
Instant finish = Instant.now();
Duration duration = Duration.between(start, finish);
long seconds = duration.getSeconds();

As expected it takes 10 seconds to run all calculations. However, we can do a lot better. We can let all 4 operations execute on their own threads which will run in parallel. Then we wait for all of them to return their results. We’ll reuse what we learnt about the ExecutorService in the previous post.

One way to solve this problem is to implement the Callable of T interface as follows:

public class CalculationServiceTask implements Callable<Integer>
{
    private final CalculationService calculationService;
    private final int firstOperand;
    private final int secondOperand;

    public CalculationServiceTask(CalculationService calculationService, int firstOperand, int secondOperand)
    {
        this.calculationService = calculationService;
        this.firstOperand = firstOperand;
        this.secondOperand = secondOperand;
    }   
    
    @Override
    public Integer call() throws Exception
    {
        return calculationService.calculate(firstOperand, secondOperand);
    }    
}

The T type parameter declares the return type. Callable has a single function called call() where we implement what and how to return. The submit() method of the executor service can accept a single Callable and return a Future of T where the Future object holds the result of the operation. In our case though it makes more sense to run the invokeAll method which accepts a collection of Callables and let them execute in parallel. It returns a collection of Future objects. We can then iterate this collection and get each result one by one:

List<Callable<Integer>> calculationTasks = new ArrayList<>();
calculationTasks.add(new CalculationServiceTask(adder, firstOperand, secondOperand));
calculationTasks.add(new CalculationServiceTask(subtractor, firstOperand, secondOperand));
calculationTasks.add(new CalculationServiceTask(multiplier, firstOperand, secondOperand));
calculationTasks.add(new CalculationServiceTask(divider, firstOperand, secondOperand));
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
try
{
    List<Future<Integer>> invokeAll = newCachedThreadPool.invokeAll(calculationTasks);

    for (Future<Integer> future : invokeAll)
    {
        int result = future.get();
        System.out.println(result);
    }
} catch (InterruptedException | ExecutionException ex)
{
    System.err.println(ex.getMessage());
}
Instant finish = Instant.now();
Duration duration = Duration.between(start, finish);
long seconds = duration.getSeconds();

This time it took 4 seconds to complete all tasks. That corresponds to the longest running task, i.e. division.

Like we saw with runnables in the previous post we can simplify our code with Lambdas in Java 8:

calculationTasks.add(() -> adder.calculate(firstOperand, secondOperand));
calculationTasks.add(() -> subtractor.calculate(firstOperand, secondOperand));
calculationTasks.add(() -> multiplier.calculate(firstOperand, secondOperand));
calculationTasks.add(() -> divider.calculate(firstOperand, secondOperand));

In that case we don’t need to implement the Callable interface at all.

View all posts related to Java here.

Advertisements

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

ultimatemindsettoday

A great WordPress.com site

iReadable { }

.NET Tips & Tricks

Robin Sedlaczek's Blog

Developer on Microsoft Technologies

HarsH ReaLiTy

A Good Blog is Hard to Find

Softwarearchitektur in der Praxis

Wissenswertes zu Webentwicklung, Domain-Driven Design und Microservices

the software architecture

thoughts, ideas, diagrams,enterprise code, design pattern , solution designs

Technology Talks

on Microsoft technologies, Web, Android and others

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

Anything around ASP.NET MVC,WEB API, WCF, Entity Framework & AngularJS

Cyber Matters

Bite-size insight on Cyber Security for the not too technical.

Guru N Guns's

OneSolution To dOTnET.

Johnny Zraiby

Measuring programming progress by lines of code is like measuring aircraft building progress by weight.

%d bloggers like this: