Runnable Interface
The Runnable interface is the preferred and most widely used way to create threads in Java. It represents a task to be executed by a thread, enabling clean design, better flexibility, and scalability. This is a very high-frequency interview topic.
What Is the Runnable Interface?
Runnable is a functional interface that represents a unit of work to be executed by a thread.
@FunctionalInterface
public interface Runnable {
void run();
}
Why Runnable Is Preferred Over Thread
- Supports multiple inheritance
- Separates task (what to do) from thread (how to run)
- Enables better object-oriented design
- Used by Executor framework
- Easier testing and reuse
Creating a Thread Using Runnable
Step-by-Step
- Implement Runnable
- Override
run() - Pass Runnable object to Thread
- Call
start()
Example
class MyTask implements Runnable {
@Override
public void run() {
System.out.println(
"Running in thread: " + Thread.currentThread().getName()
);
}
}
public class Test {
public static void main(String[] args) {
Thread t = new Thread(new MyTask());
t.start();
}
}
- ✔ New thread created
- ✔
run()executed by JVM
Using Runnable with Lambda (Java 8+)
Because Runnable is a functional interface:
Thread t = new Thread(() -> {
System.out.println("Thread using lambda");
});
t.start();
- ✔ Clean
- ✔ Concise
- ✔ Modern
run() vs start() (Critical Interview Point)
t.run(); // ❌ no new thread
t.start(); // ✔ new thread
| Method | Behavior |
|---|---|
run() |
Normal method call |
start() |
Creates new thread |
Runnable and Thread Relationship
Runnable (Task)
↓
Thread (Executor)
- ✔ Runnable contains logic
- ✔ Thread executes logic
Multiple Threads Sharing Same Runnable
Runnable task = new MyTask();
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
- ✔ Same task
- ✔ Multiple threads
- ⚠ Requires synchronization for shared data
Runnable vs Thread (Interview Table)
| Aspect | Runnable | Thread |
|---|---|---|
| Type | Interface | Class |
| Inheritance | Supports | Does not |
| Code separation | ✔ Yes | ❌ No |
| Reusability | High | Low |
| Recommended | ✔ Yes | ❌ Less |
Runnable vs Callable (Preview)
| Feature | Runnable | Callable |
|---|---|---|
| Return value | ❌ No | ✔ Yes |
| Checked exception | ❌ No | ✔ Yes |
| Used with Executor | ✔ Yes | ✔ Yes |
Common Beginner Mistakes
- Calling
run()directly - Extending Thread unnecessarily
- Sharing mutable state without synchronization
- Ignoring thread safety
- Forgetting thread naming
Best Practices
- Prefer Runnable for thread creation
- Use ExecutorService for production code
- Keep
run()short and focused - Avoid heavy logic inside
run() - Handle exceptions inside
run()
Runnable Examples and Common Scenarios
1. Basic Runnable Implementation
class MyTask implements Runnable {
public void run() {
System.out.println("Runnable running");
}
public static void main(String[] args) {
Thread t = new Thread(new MyTask());
t.start();
}
}
2. Runnable with Lambda Expression
class Demo {
public static void main(String[] args) {
Runnable r = () -> System.out.println("Lambda Runnable");
new Thread(r).start();
}
}
3. Passing Runnable Directly to Thread
class Demo {
public static void main(String[] args) {
new Thread(() -> System.out.println("Inline Runnable")).start();
}
}
4. Runnable vs Thread (Inheritance Limitation)
class Task extends SomeOtherClass implements Runnable {
public void run() {
System.out.println("Runnable allows multiple inheritance");
}
}
Explanation
- Java allows only one class
- Runnable avoids inheritance restriction
5. Same Runnable Shared by Multiple Threads
class Counter implements Runnable {
int count = 0;
public void run() {
count++;
}
public static void main(String[] args) throws Exception {
Counter task = new Counter();
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(task.count);
}
}
Explanation
- Same object shared → shared memory
6. Synchronizing Runnable (Thread Safety)
class Counter implements Runnable {
int count = 0;
public synchronized void run() {
count++;
}
public static void main(String[] args) throws Exception {
Counter task = new Counter();
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(task.count);
}
}
7. Runnable with sleep()
class Demo {
public static void main(String[] args) {
Runnable r = () -> {
try {
Thread.sleep(300);
System.out.println("After sleep");
} catch (Exception e) {}
};
new Thread(r).start();
}
}
8. Runnable with Thread Name
class Demo {
public static void main(String[] args) {
Runnable r = () ->
System.out.println(Thread.currentThread().getName());
Thread t = new Thread(r, "Worker-1");
t.start();
}
}
9. Runnable with Exception Handling
class Demo {
public static void main(String[] args) {
Runnable r = () -> {
throw new RuntimeException("Error");
};
Thread t = new Thread(r);
t.setUncaughtExceptionHandler(
(th, ex) -> System.out.println("Handled: " + ex.getMessage()));
t.start();
}
}
10. Runnable in Loop
class Demo {
public static void main(String[] args) {
for (int i = 1; i <= 3; i++) {
int id = i;
new Thread(() -> System.out.println("Task " + id)).start();
}
}
}
11. Runnable vs Callable (No Return)
Runnable r = () -> System.out.println("No return");
Explanation
- Runnable cannot return value
- Use Callable if return is needed
12. Runnable Executed by ExecutorService
import java.util.concurrent.*;
class Demo {
public static void main(String[] args) {
ExecutorService ex = Executors.newSingleThreadExecutor();
ex.execute(() -> System.out.println("Runnable via Executor"));
ex.shutdown();
}
}
13. Runnable with Shared Object Lock
class Task implements Runnable {
public void run() {
synchronized (this) {
System.out.println("Thread-safe block");
}
}
}
14. ❌ Calling run() Directly (Common Trap)
class Demo {
public static void main(String[] args) {
Runnable r = () -> System.out.println("Thread?");
r.run(); // ❌ no new thread
}
}
15. Runnable with yield()
class Demo {
public static void main(String[] args) {
Runnable r = () -> {
System.out.println("Before yield");
Thread.yield();
System.out.println("After yield");
};
new Thread(r).start();
}
}
16. Runnable with join()
class Demo {
public static void main(String[] args) throws Exception {
Thread t = new Thread(() -> {
try { Thread.sleep(300); } catch (Exception e) {}
System.out.println("Worker done");
});
t.start();
t.join();
System.out.println("Main resumed");
}
}
17. Runnable with Volatile Variable
class Task implements Runnable {
volatile boolean running = true;
public void run() {
while (running) {}
}
public static void main(String[] args) {
Task t = new Task();
new Thread(t).start();
t.running = false;
}
}
18. Multiple Runnable Objects, Multiple Threads
class Demo {
public static void main(String[] args) {
new Thread(() -> System.out.println("Task A")).start();
new Thread(() -> System.out.println("Task B")).start();
}
}
19. Real Interview Example
Runnable task = () -> System.out.println("Parallel execution");
Usage
- Selenium parallel tests
- Background jobs
- Logging threads
20. Interview Summary – Runnable
Runnable r;
Thread t = new Thread(r);
Key Points
- Functional interface (run())
- No return value
- Supports lambda
- Preferred over Thread
- Enables shared state
Interview-Ready Answers
Short Answer
Runnable is an interface used to define a task that can be executed by a thread.
Detailed Answer
In Java, the Runnable interface represents a unit of work to be executed by a thread. It defines a single
run() method and is preferred over extending Thread because it supports multiple inheritance,
improves design, and is widely used in modern concurrency frameworks.
Key Takeaway
Runnable defines the task.
Thread runs the task.
Understanding Runnable is essential for scalable, maintainable, and testable Java multithreading.