Submitting Tasks: Runnable vs Callable
Submit Runnable and Callable tasks, and retrieve results via Future.
Submitting Tasks: Runnable vs Callable is a free Java Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Java Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Runnable vs Callable
Two functional interfaces represent tasks submitted to ExecutorService:
- Runnable: no return value, cannot declare checked exceptions
- Callable<V>: returns a result of type V, can throw checked exceptions
import java.util.concurrent.*;
// Runnable — no return value
Runnable r = () -> System.out.println("Running");
// Callable<Integer> — returns a value
Callable<Integer> c = () -> {
return 42; // or do computation
};submit(Runnable)
submit(Runnable) returns a Future<?>. Calling get() on it returns null but confirms the task completed (or throws if it failed).
ExecutorService pool = Executors.newFixedThreadPool(2);
Future<?> future = pool.submit(() ->
System.out.println("Task done on " + Thread.currentThread().getName())
);
future.get(); // waits for completion; returns null
System.out.println("Task confirmed complete");
pool.shutdown();submit(Callable<V>)
submit(Callable) returns a Future<V>. Call future.get() to retrieve the result (blocks until complete).
ExecutorService pool = Executors.newFixedThreadPool(2);
Callable<Integer> sumTask = () -> {
int sum = 0;
for (int i = 1; i <= 100; i++) sum += i;
return sum;
};
Future<Integer> future = pool.submit(sumTask);
Integer result = future.get(); // blocks until done
System.out.println("Sum 1..100 = " + result); // 5050
pool.shutdown();Submitting Multiple Callables with invokeAll
invokeAll() submits a collection of Callables and returns a List of Futures — all complete before returning:
List<Callable<String>> tasks = List.of(
() -> "Result A",
() -> "Result B",
() -> "Result C"
);
ExecutorService pool = Executors.newFixedThreadPool(3);
List<Future<String>> futures = pool.invokeAll(tasks);
for (Future<String> f : futures) {
System.out.println(f.get());
}
pool.shutdown();invokeAny: First Successful Result
invokeAny() submits a collection of Callables and returns the result of the first one that completes successfully, cancelling the rest:
List<Callable<String>> candidates = List.of(
() -> { Thread.sleep(200); return "Slow"; },
() -> { Thread.sleep(50); return "Fast"; },
() -> { Thread.sleep(100); return "Medium"; }
);
ExecutorService pool = Executors.newFixedThreadPool(3);
String winner = pool.invokeAny(candidates);
System.out.println("First: " + winner); // Fast
pool.shutdown();execute() vs submit()
execute(Runnable) fires and forgets — no Future returned, exceptions are lost unless you add an uncaught exception handler. submit() wraps the task in a Future, capturing exceptions for retrieval via get().
ExecutorService pool = Executors.newFixedThreadPool(2);
// execute: exception silently lost
pool.execute(() -> { throw new RuntimeException("Oops!"); });
// submit: exception captured in Future
Future<?> f = pool.submit(() -> { throw new RuntimeException("Oops!"); });
try {
f.get();
} catch (ExecutionException e) {
System.out.println("Caught: " + e.getCause().getMessage());
}
pool.shutdown();Callable with Checked Exceptions
Unlike Runnable, Callable can throw checked exceptions — making it natural for I/O and database operations:
Callable<String> dbQuery = () -> {
// This compiles fine — checked exception is declared on Callable.call()
if (Math.random() < 0.5) throw new java.sql.SQLException("DB error");
return "row data";
};
Future<String> f = pool.submit(dbQuery);
try {
String data = f.get();
} catch (ExecutionException e) {
if (e.getCause() instanceof java.sql.SQLException) {
System.out.println("DB failed: " + e.getCause().getMessage());
}
}Parallelizing Independent Computations
Use multiple Callable submissions to run independent computations in parallel:
ExecutorService pool = Executors.newFixedThreadPool(4);
Future<Long> sumFuture = pool.submit(() -> LongStream.rangeClosed(1,1_000_000).sum());
Future<Long> prodFuture = pool.submit(() -> LongStream.rangeClosed(1,20).reduce(1L, (a,b)->a*b));
long sum = sumFuture.get();
long prod = prodFuture.get();
System.out.println("Sum: " + sum + ", 20!: " + prod);
pool.shutdown();Callable Result Aggregation
Submit many tasks and aggregate results once all are done:
int N = 8;
ExecutorService pool = Executors.newFixedThreadPool(N);
List<Future<Integer>> futures = new ArrayList<>();
for (int i = 0; i < N; i++) {
final int chunk = i;
futures.add(pool.submit(() -> chunk * chunk)); // i^2
}
int total = 0;
for (Future<Integer> f : futures) total += f.get();
System.out.println("Sum of squares: " + total);
pool.shutdown();Timeout on Callable
Pass a timeout to future.get(timeout, unit) to avoid waiting forever:
Future<String> f = pool.submit(() -> {
Thread.sleep(5000); // slow task
return "done";
});
try {
String result = f.get(1, TimeUnit.SECONDS);
} catch (TimeoutException e) {
System.out.println("Task timed out");
f.cancel(true); // interrupt the task
} catch (ExecutionException e) {
System.out.println("Task failed: " + e.getCause());
}Best Practices
Summary of best practices:
- Use
submit()overexecute()— exceptions are captured - Use Callable when you need a return value or checked exceptions
- Always call
future.get()with a timeout - Use invokeAll for batch processing; invokeAny for first-response-wins
- Always shut down the pool
Quick Check
What is the key advantage of Callable over Runnable?
Recap: Runnable vs Callable
Key takeaways:
- Runnable: void, no checked exceptions; submit returns Future> null
- Callable: returns V, can throw checked exceptions; submit returns Future
- invokeAll: submits batch, waits for all to finish
- invokeAny: returns first successful result, cancels rest
- Always use submit() for exception capture
Frequently asked questions
Is the “Submitting Tasks: Runnable vs Callable” lesson free?
Yes — the full text of “Submitting Tasks: Runnable vs Callable” is free to read here on the web, and the Java Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Java Academy course, upgrade to CoddyKit PRO.
What will I learn in “Submitting Tasks: Runnable vs Callable”?
Submit Runnable and Callable tasks, and retrieve results via Future. You practise Java Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Java Academy?
No prior experience is required. Java Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Submitting Tasks: Runnable vs Callable” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Java Academy lesson?
Yes. Every Java Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- ExecutorService and Thread Pool Types
- Submitting Tasks: Runnable vs Callable
- Future and Error Handling
- ScheduledExecutorService for Recurring Tasks