Difference between Runnable and Thread in Java

In Java, multithreading is one of the core features that allows programs to perform multiple tasks simultaneously. When learning how to create threads, you’ll encounter two commonly used approaches:

  • Extending the Thread class
  • Implementing the Runnable interface

Both are used to define and execute tasks in separate threads, but they are not the same and serve different design purposes.

In this article, we will explain the difference between Runnable and Thread in Java, their advantages and disadvantages, and when to use one over the other — with examples and a clear comparison table.

Introduction to Threading in Java

Multithreading in Java allows multiple parts of a program to execute concurrently, making applications more responsive and efficient — especially for tasks like I/O operations, file processing, or background services.

Java provides two main ways to create threads:

  1. By extending the Thread class
  2. By implementing the Runnable interface

Let’s look at both.

What is Thread in Java?

Thread is a class in the java.lang package that represents a single thread of execution. You can create a thread by extending this class and overriding the run() method.

Example: Using Thread Class

class MyThread extends Thread {
public void run() {
System.out.println("Thread is running: " + Thread.currentThread().getName());
}
}

public class ThreadExample {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start(); // Starts a new thread
}
}

Output:

Thread is running: Thread-0

Key Points:

  • You extend the Thread class.
  • You override the run() method to define the thread’s task.
  • You call start() to begin the thread’s execution.

What is Runnable in Java?

Runnable is a functional interface in Java that represents a task that can be executed by a thread. Instead of extending Thread, you implement Runnable and pass the object to a Thread instance.

Example: Using Runnable Interface

class MyTask implements Runnable {
public void run() {
System.out.println("Runnable is running: " + Thread.currentThread().getName());
}
}

public class RunnableExample {
public static void main(String[] args) {
Runnable task = new MyTask();
Thread t1 = new Thread(task);
t1.start(); // Starts a new thread
}
}

Output:

Runnable is running: Thread-0

Key Points:

  • You implement the Runnable interface.
  • The thread logic goes inside the run() method.
  • A new Thread object is created using the Runnable object.

📊 Comparison: Runnable vs Thread

Why Prefer Runnable Over Thread?

✅ 1. Java Supports Single Inheritance

Java doesn’t support multiple inheritance. If your class extends Thread, it cannot extend any other class. But if your class implements Runnable, it’s free to extend another class too.

class Base {}
class Task extends Base implements Runnable {
public void run() {
System.out.println("Running task...");
}
}

This adds more flexibility and reusability.

✅ 2. Separation of Concerns

When you use Runnable, the task (business logic) is separated from the thread (execution mechanism). This is a cleaner design because:

  • You can reuse the task with different threads.
  • You can execute the same task in different ways (e.g., in a thread pool).

✅ 3. Thread Pool Compatibility

Java’s Executor Framework (introduced in Java 5) works with Runnable, not Thread.

ExecutorService executor = Executors.newFixedThreadPool(2);
executor.submit(new MyTask()); // MyTask implements Runnable

This is a scalable and efficient way to manage threads — a feature not available with custom Thread subclasses.

Running Multiple Threads

Let’s see how both approaches run multiple threads:

✅ Using Thread:

class MyThread extends Thread {
public void run() {
System.out.println("Thread running: " + Thread.currentThread().getName());
}
}

public class TestThread {
public static void main(String[] args) {
new MyThread().start();
new MyThread().start();
}
}

✅ Using Runnable:

class MyRunnable implements Runnable {
public void run() {
System.out.println("Runnable running: " + Thread.currentThread().getName());
}
}

public class TestRunnable {
public static void main(String[] args) {
Runnable r = new MyRunnable();
new Thread(r).start();
new Thread(r).start();
}
}

Both methods produce similar output, but the Runnable approach is more scalable and modular.

❌ Common Mistakes to Avoid

1. Calling run() Instead of start()

Thread t = new Thread(() -> System.out.println("Wrong"));
t.run(); // ❌ Executes in main thread (no multithreading)

✅ Always use start() to run in a separate thread.

2. Forgetting to Implement run() in Runnable

class Task implements Runnable {
// Missing run() method – won't compile!
}

✅ Always override the run() method.

🔁 Summary Table

✅ Final Thoughts

While both Thread and Runnable can be used to create and run threads in Java, they serve different purposes:

  • Use Thread when you have a quick, small task and no need to extend another class.
  • Use Runnable when you want cleaner code, better reusability, and compatibility with executors and thread pools.

Best Practice:

Always prefer implementing Runnable over extending Thread in real-world applications.

It gives your code more flexibility, aligns with modern Java concurrency features, and promotes better object-oriented design.

Comments

Spring Boot 3 Paid Course Published for Free
on my Java Guides YouTube Channel

Subscribe to my YouTube Channel (165K+ subscribers):
Java Guides Channel

Top 10 My Udemy Courses with Huge Discount:
Udemy Courses - Ramesh Fadatare