Runnable
The Runnable interface — the standard way to define a task for concurrent execution in Java.
Last reviewed: January 2025
🔍 What Is It?
Runnable interface:
@FunctionalInterface
public interface Runnable {
void run();
}Callable interface (Runnable with return value):
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}Key differences:
| Runnable | Callable<V> | |
|---|---|---|
| Return value | void | V |
| Checked exceptions | No | Yes (throws Exception) |
| Used with | Thread, ExecutorService | ExecutorService |
| Returns | nothing | Future<V> |
Future<V>:
ExecutorService.submit(Callable) returns a Future<V>. Call future.get() to retrieve the result (blocks until done) or future.get(timeout, unit) for timeout.
🎯 Why Do We Need It?
Runnable separates the task definition from the execution mechanism. You define what to do (Runnable), and the ExecutorService decides when and how to run it (thread pool management). This separation makes code more testable and flexible.
🏗️ What Happens in Real Projects?
Common patterns:
// Spring @Async — Spring wraps your method in a Runnable
@Async
public void sendWelcomeEmail(String email) {
emailService.send(email); // runs in Spring's async thread pool
}
// ExecutorService with Runnable
executor.submit(() -> processOrder(orderId));
// ExecutorService with Callable (returns result)
Future<String> future = executor.submit(() -> {
return fetchUserFromApi(userId); // may throw Exception
});
String user = future.get(5, TimeUnit.SECONDS);⚙️ How It Works
Thread.start() vs ExecutorService.submit():
- →Thread.start() creates a new OS thread for each task — expensive
- →ExecutorService.submit() reuses threads from the pool — efficient
Checked exceptions in Runnable:
Runnable.run() cannot throw checked exceptions. Options:
- Catch and handle inside run()
- Catch and wrap in RuntimeException
- Use Callable instead (allows throws Exception)
- Use CompletableFuture (handles exceptions properly)
💻 Example
Runnable and Callable Patterns — How to define and submit tasks for concurrent execution.
🤔 Common Confusions
❓ When should I use Runnable vs Callable vs CompletableFuture?
Use Runnable for fire-and-forget tasks with no return value. Use Callable when you need a return value and Future.get() blocking is acceptable. Use CompletableFuture for async pipelines, non-blocking composition, and proper exception handling. In modern Java, CompletableFuture is preferred over Callable+Future for most use cases.
🚨 Common Production Issues
Future.get() blocking the calling thread — defeats the purpose of async. Use CompletableFuture for non-blocking composition.
Uncaught exceptions in Runnable swallowed by ExecutorService — exceptions thrown from Runnable.run() are caught by the executor and not propagated. Use Future.get() to check for exceptions, or set an UncaughtExceptionHandler.
✅ Best Practices
Prefer CompletableFuture over Callable+Future for new code.
Use @Async in Spring for simple fire-and-forget async methods.
Always handle InterruptedException and restore the interrupt flag.
Set meaningful thread names in ExecutorService for debugging.
🏛️ Architect's Note
Runnable and Callable are low-level primitives. In Spring Boot, you rarely use them directly. Use @Async for fire-and-forget, CompletableFuture for async with result, and Spring Batch for batch processing. These abstractions handle thread pool management, exception handling, and monitoring for you.
💬 Frequently Asked Questions
🎯 Key Takeaways
🔗 Related Topics
🎯 Practice this topic in interviews
Core Java Interview Prep
Practice interview questions that connect directly to this technology track.
↗ Where this topic fits
Core Java Technology Guide
Return to the Core Java learning hub.
Backend Engineer Career Path
A structured journey through Core Java, Spring Boot, production backend engineering, and interview preparation.
Full Stack Java Developer Career Path
A guided route through frontend foundations, Java backend engineering, delivery workflows, and interview readiness.
Sources
Sources / References
Use these Core Java references to verify the concept in official docs and continue into the most relevant SplashRide pages.
Official documentation
Related SplashRide pages
SplashRide Core Java learning path
Browse the full Core Java topic collection and learning order on SplashRide.
Core Java Interview Prep
Practice Core Java interview questions and scenario-based answers.
Backend Engineer Career Path
A structured journey through Core Java, Spring Boot, production backend engineering, and interview preparation.
Full Stack Java Developer Career Path
A guided route through frontend foundations, Java backend engineering, delivery workflows, and interview readiness.
Threads
Java threads — creating and managing threads, thread lifecycle, and the concurrency problems every enterprise developer must understand.
Executor Framework
The Executor framework — thread pools, ExecutorService types, and how to configure them for production workloads.