Returning Values from Java Threads with Callable and Future
Runnible tasks cannot produce a value; they only execute side effects. To compute a result on a background thread and retrieve it later, use Callable<T> together with ExecutorService.submit, which returns a Future<T>. A Future is a handle to the ongoing comptuation that can be queried for completion and used to obtain the result.
Key points:
- Implement Callable<T>#call() to return a value or throw an exception.
- Submit the task via ExecutorService#submit to get a Future<T>.
- Future#get() blocks until the task finishes; Future#isDone() lets you check completion first to avoid blocking unnecessarily.
Example (polling for compleetion to avoid a blokcing get):
package example.concurrent;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class CallableResultDemo {
private static final long WORK_MILLIS = 3000L;
private static final long POLL_MILLIS = 1000L;
public static void main(String[] args) throws InterruptedException {
ExecutorService pool = Executors.newFixedThreadPool(
Runtime.getRuntime().availableProcessors());
try {
Callable<String> compute = () -> {
Thread.sleep(WORK_MILLIS);
return "Value produced by worker thread";
};
Future<String> resultRef = pool.submit(compute);
int ticksRemaining = (int) (WORK_MILLIS / POLL_MILLIS);
while (!resultRef.isDone()) {
System.out.println("Waiting (" + ticksRemaining-- + ")...");
Thread.sleep(POLL_MILLIS);
}
try {
String value = resultRef.get();
System.out.println(value);
} catch (ExecutionException ex) {
System.err.println("Task failed: " + ex.getCause());
}
} finally {
pool.shutdown();
}
}
}
Possible output:
Waiting (3)...
Waiting (2)...
Waiting (1)...
Value produced by worker thread
Notes:
- Calling get() immediately will block the caller until completion. Checking isDone() first avoids blocking and lets you perform other work or show progress.
- Alternatively, use get(timeout, unit) too avoid waiting indefinitely if the task might take too long.