Differences between “implements Runnable” and “extends Thread” in Java?

Here’s a detailed comparison of implements Runnable vs. extends Thread in Java, with practical examples and scenarios:

Key Differences

Featureimplements Runnableextends Thread
Inheritance✅ Can extend another class.❌ Blocks other inheritance (single parent).
Reusability✅ Same task can run in multiple threads.❌ Each thread is a unique instance.
Separation of Concerns✅ Separates task logic from thread mechanics.❌ Combines task and thread logic.
Lambda Support✅ Works with lambdas/functional interfaces.❌ Requires subclassing.
Thread Pools✅ Compatible with ExecutorService.❌ Not ideal for modern thread pools.

Examples

1. Basic Implementation

Using Runnable (recommended):

public class MyTask implements Runnable {
    @Override
    public void run() {
        System.out.println("Running via Runnable: " + Thread.currentThread().getName());
    }
}

// Usage:
Thread t1 = new Thread(new MyTask(), "Thread-1");
t1.start(); // Starts a new thread executing MyTask's run()

Using Thread Subclass:

public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Running via Thread: " + getName());
    }
}

// Usage:
MyThread t2 = new MyThread();
t2.start(); // Starts the thread

2. Sharing a Task Across Multiple Threads

Only possible with Runnable:

public class CounterTask implements Runnable {
    private int count = 0;

    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            System.out.println(Thread.currentThread().getName() + ": " + count++);
        }
    }
}

// Share the same task between two threads:
CounterTask sharedTask = new CounterTask();
Thread worker1 = new Thread(sharedTask, "Worker-1");
Thread worker2 = new Thread(sharedTask, "Worker-2");
worker1.start();
worker2.start();

// Output (example):
// Worker-1: 0
// Worker-2: 1
// Worker-1: 2
// Worker-2: 3
// Worker-1: 4
// Worker-2: 5

3. Using Lambdas with Runnable

Java 8+ allows concise syntax for Runnable:

// Lambda Runnable
Runnable task = () -> {
    System.out.println("Running via lambda: " + Thread.currentThread().getName());
};

// Start thread directly:
new Thread(task, "Lambda-Thread").start();

4. Thread Pools (ExecutorService)

Only works with Runnable/Callable:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

ExecutorService executor = Executors.newFixedThreadPool(2);

// Submit Runnable tasks:
executor.submit(() -> System.out.println("Task 1"));
executor.submit(() -> System.out.println("Task 2"));

executor.shutdown();

5. Extending Another Class

Only possible with Runnable:

public class MyApp extends JFrame implements Runnable {
    // Extends JFrame (GUI) and implements Runnable
    @Override
    public void run() {
        // Background task for the GUI
        System.out.println("GUI task running...");
    }
}

// Usage:
MyApp app = new MyApp();
new Thread(app).start();

6. Overriding Thread.start() (Risky)

Possible but discouraged with Thread subclass:

public class CustomThread extends Thread {
    @Override
    public void run() {
        System.out.println("CustomThread running");
    }

    @Override
    public void start() { // ❌ Overriding start() is risky
        System.out.println("Custom start()");
        super.start(); // Must call super.start() to spawn a thread
    }
}

// Usage:
CustomThread t = new CustomThread();
t.start(); // Output: "Custom start()" followed by "CustomThread running"

When to Use Which

  • Prefer Runnable for:
  • Tasks needing reuse across threads.
  • Modern thread pools (ExecutorService).
  • Classes that must extend another superclass.
  • Clean separation of task and threading logic.
  • Avoid Thread Subclass except for:
  • Trivial, single-use threads (rare).
  • Legacy code requiring direct Thread method access.

Best Practices

  1. Favor Runnable for flexibility and modern concurrency.
  2. Use thread pools (ExecutorService) instead of raw Thread management.
  3. Avoid overriding Thread.start() unless you fully understand threading mechanics.

By choosing Runnable, you write cleaner, more maintainable code that aligns with Java’s concurrency best practices.

Leave a Reply

Your email address will not be published. Required fields are marked *