← Back to Home

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

  1. Implement Runnable
  2. Override run()
  3. Pass Runnable object to Thread
  4. 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.