Problem:
How to use Java Callable to run tasks that actually return results? In this post we’ll show how to use ExecutorService to run tasks in separate threads to compute and return results.
Solution:
In the following example we’re going to use another feature from fantastic java.util.concurrent package: Callable<T>. The interface is a sister of Runnable, which you could see in action in our previous posts (Java Runnable, Java ExecutorService, and others). There are a couple of differences between them:
- Callable<T> returns a Future<T> that will return a result… in the future. :-)
- Callable<T> is parametrized with result’s type.
- Callable’s method is called call() and may throw an Exception.
To perform our computation we’re going to use ExecutorService‘s. The method for Callables is <T> Future<T> submit(Callable<T>), which just submits given task to the executor for execution in a thread, and returns an object (Future<T>), which will have a result of computation when it’s finished.
Enough talking, here’s the code:
package com.farenda.java.util.concurrent; import java.util.LinkedList; import java.util.List; import java.util.Random; import java.util.concurrent.*; import static java.util.concurrent.TimeUnit.MILLISECONDS; public class CallableExample { private static class ComputingTask implements Callable<Integer> { private static int nth = 0; private final int id = ++nth; @Override public Integer call() throws Exception { int value = new Random().nextInt(1000); try { System.out.printf("Task %d started computing...%n", id); MILLISECONDS.sleep(value); } catch (InterruptedException e) { // ignore interruptions } System.out.printf("Task %d is returning value: %d%n", id, value); return value; } } public static void main(String[] args) { ExecutorService executor = Executors.newCachedThreadPool(); System.out.println("Submitting tasks for execution:"); List<Future<Integer>> results = new LinkedList<>(); for (int i = 0; i < 5; ++i) { results.add(executor.submit(new ComputingTask())); } System.out.println("Getting results from futures:"); for (Future<Integer> result : results) { try { System.out.printf("computed result: %d%n", result.get()); } catch (InterruptedException e) { System.out.println("Interrupted while waiting for result: " + e.getMessage()); } catch (ExecutionException e) { System.out.println("A task ended up with an exception: " + e.getCause()); } } System.out.println("Shutting down the executor."); executor.shutdown(); } }
As you can see the code is straightforward. We’re creating task, submit them to the executor and collect futures. Later we’re going through futures to get results. To do that we’re using get() method, which blocks and waits for the result. If you don’t want to wait indefinitely long, you can use get(long timeout, TimeUnit unit) that allows to wait only for specified time. There’s also another useful method – isDone(). It allows to check without blocking whether a future already has a result.
Here’s the result of running the above code:
Submitting tasks for execution: Getting results from futures: Task 2 started computing... Task 1 started computing... Task 4 started computing... Task 5 started computing... Task 3 started computing... Task 5 is returning value: 85 Task 1 is returning value: 117 computed result: 117 Task 3 is returning value: 165 Task 4 is returning value: 405 Task 2 is returning value: 603 computed result: 603 computed result: 165 computed result: 405 computed result: 85 Shutting down the executor.
Nice and useful feature. Stay tuned for more posts about Java Concurrency.