Java Notes
Complete guide to the Runnable interface in Java — functional interface, lambda usage, Callable vs Runnable, task encapsulation, sharing Runnables, and modern patterns with ExecutorService.
What is the Runnable Interface?
Runnable is a functional interface in java.lang package with a single abstract method run(). It represents a task that can be executed by a thread.
@FunctionalInterface
public interface Runnable {
void run();
}Why Runnable is Preferred Over Extending Thread
- Single inheritance: Your class can still extend another class
- Separation of concerns: Task logic is separate from threading
- Reusability: Same Runnable can be used with Thread, ExecutorService, etc.
- Composition: Multiple tasks can be composed together
- Testability: Easy to test without running in a separate thread
Basic Runnable Implementation
Try it in WoHoCode
Edit the program, then run it here.
Run this example to see its output.
Runnable with Lambda Expressions
Since Runnable is a functional interface, lambdas make it concise:
Try it in WoHoCode
Edit the program, then run it here.
Run this example to see its output.
Runnable vs Callable
Java 5 introduced Callable<V> — a Runnable that can return a value and throw exceptions:
Try it in WoHoCode
Edit the program, then run it here.
Run this example to see its output.
Composing Runnables
Try it in WoHoCode
Edit the program, then run it here.
Run this example to see its output.
Output:
Real-World Example: Task Queue
Try it in WoHoCode
Edit the program, then run it here.
Run this example to see its output.
Common Mistakes
1. Confusing Runnable execution
Runnable task = () -> System.out.println("Thread: " + Thread.currentThread().getName());
// ❌ This does NOT create a new thread
task.run(); // Runs on current thread
// ✅ This creates a new thread
new Thread(task).start();2. Modifying shared state without synchronization
Interview Questions
Q1: What is the Runnable interface?
Answer: Runnable is a functional interface in java.lang with a single method void run(). It represents a task to be executed by a thread. Since Java 8, it can be implemented using lambda expressions.
Q2: Why is Runnable preferred over extending Thread?
Answer: (1) Allows extending another class (Java lacks multiple inheritance), (2) separates task logic from thread management, (3) same Runnable works with Thread, ExecutorService, ForkJoinPool, (4) promotes composition and reusability, (5) easier to test.
Q3: What is the difference between Runnable and Callable?
Answer: Runnable's run() returns void and cannot throw checked exceptions. Callable's call() returns a generic type V and can throw Exception. Callable is used with ExecutorService to get Future objects containing results.
Q4: Can a Runnable be reused across multiple threads?
Answer: Yes! A single Runnable instance can be passed to multiple Thread constructors. Each thread will execute the same run() method independently. However, if the Runnable has mutable state, proper synchronization is needed.
Q5: Is Runnable a functional interface?
Answer: Yes. It has exactly one abstract method (run()), making it usable with lambda expressions and method references since Java 8. Example: Runnable r = () -> System.out.println("Hello");
Q6: Why is Runnable a functional interface?
A: Runnable has exactly one abstract method (run()), making it a functional interface annotated with @FunctionalInterface. This means it can be implemented using lambda expressions or method references in Java 8+: new Thread(() -> doWork()).start(). This dramatically reduces boilerplate for simple thread tasks.
Q7: Can a single Runnable instance be shared among multiple threads?
A: Yes! Unlike extending Thread (where each instance IS a thread), a single Runnable can be passed to multiple Thread constructors. All threads execute the same run() method. This is useful for tasks that share resources, but requires synchronization if the Runnable has mutable state.
Q8: How does Runnable work with ExecutorService?
A: ExecutorService accepts Runnable tasks via execute(Runnable) or submit(Runnable). The executor manages a thread pool — your Runnable runs on a pooled thread instead of creating new threads. submit() returns a Future<?> for tracking completion (though Runnable doesn't return a value).
Q9: What is the difference between Runnable and Callable?
A: Runnable: void run() — no return value, cannot throw checked exceptions. Callable: V call() throws Exception — returns a value of type V, can throw checked exceptions. Use Callable when you need to get a result back from the thread (via Future.get()).
Q10: How do you convert a Runnable to a Callable?
A: Use Executors.callable(runnable) which returns a Callable that returns null. Or Executors.callable(runnable, result) which returns a specified result. You can also wrap manually: () -> { runnable.run(); return result; }.
Q11: Is Runnable's run() method thread-safe?
A: The run() method itself has no built-in thread safety. If the Runnable accesses shared mutable state, YOU must provide synchronization (synchronized blocks, volatile, Atomic classes, etc.). Runnable is just a task definition — thread safety is the developer's responsibility.
Continue the lesson path