0
Explore
0

Runnable Interface in Java – A Complete Tutorial

Updated on August 1, 2025

Multithreading is a powerful feature of Java that allows concurrent execution of two or more parts of a program. One of the foundational components of Java’s multithreading is the Runnable interface.

What is the Runnable Interface?

The Runnable interface is a functional interface in Java defined in the java.lang package. It is used to represent a task that can be executed by a thread.

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

As you can see, the interface has only one method, run(). This method is meant to contain the code that you want the thread to execute.

Why Use Runnable?

You use Runnable when:

  • You want to execute code in a separate thread.
  • You want to share a common resource or object among multiple threads.
  • You want to inherit from another class (since Java doesn’t support multiple inheritance of classes).

Runnable vs Extending Thread Class

FeatureRunnable InterfaceThread Class
InheritanceCan extend another classCannot extend any other class
Code SharingEasier to share resourcesNot easy to share resources
Recommended UsePreferred in real-world projectsMostly used in simple cases or learning
FlexibilityHigh (only implements interface)Low (inherits whole thread class)

How to Implement Runnable in Java

Example 1: Creating and Running a Thread Using Runnable

class MyRunnable implements Runnable {
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println("Runnable thread: " + i);
        }
    }
}

public class RunnableExample {
    public static void main(String[] args) {
        MyRunnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);
        thread.start();  // starts a new thread
    }
}

Output:

Runnable thread: 1
Runnable thread: 2
Runnable thread: 3
Runnable thread: 4
Runnable thread: 5

Explanation

  • Implements Runnable:
    MyRunnable class implements the Runnable interface, meaning it must define a run() method that contains the code to be run by a thread.
  • run() Method:
    This method prints “Runnable thread: 1” to “Runnable thread: 5” using a loop.
  • Creating Thread:
    In main(), we create an object of MyRunnable and pass it to a Thread constructor.
  • Starting Thread:
    thread.start() starts a new thread and internally calls the run() method.

Example 2: Printing a Welcome Message for Multiple Users

Imagine you’re building a system where multiple users log into a website, and each user should be welcomed in a separate thread. We’ll simulate this by using the Runnable interface to print a welcome message multiple times — as if greeting each user independently in a thread.

// Step 1: Create a class that implements Runnable
class WelcomeTask implements Runnable {

    private String userName;

    // Constructor to receive the user's name
    WelcomeTask(String userName) {
        this.userName = userName;
    }

    // Step 2: Override the run() method to define thread's behavior
    public void run() {
        for (int i = 1; i <= 3; i++) {
            System.out.println("Welcome " + userName + " - message " + i);
            try {
                Thread.sleep(500); // adding delay to simulate processing
            } catch (InterruptedException e) {
                System.out.println("Thread interrupted for user: " + userName);
            }
        }
    }
}

// Step 3: Create and start threads in the main class
public class RunnableExample {
    public static void main(String[] args) {
        // Creating tasks for different users
        WelcomeTask user1 = new WelcomeTask("Alice");
        WelcomeTask user2 = new WelcomeTask("Bob");

        // Wrapping each task in a Thread object
        Thread thread1 = new Thread(user1);
        Thread thread2 = new Thread(user2);

        // Starting both threads
        thread1.start();
        thread2.start();
    }
}

Output (Note: Output order may vary due to thread scheduling.)

Welcome Alice - message 1
Welcome Bob - message 1
Welcome Alice - message 2
Welcome Bob - message 2
Welcome Alice - message 3
Welcome Bob - message 3

Step-by-Step Explanation:

1. class WelcomeTask implements Runnable

  • We’re creating a class WelcomeTask that implements the Runnable interface.
  • This class will define what task we want to perform in a separate thread.

2. Constructor WelcomeTask(String userName)

  • We use a constructor to initialize the user’s name.
  • Each instance of WelcomeTask represents a specific user.

3. public void run()

  • The run() method contains the code that will execute in the new thread.
  • It prints a welcome message three times for the specific user.
  • Thread.sleep(500) pauses the thread for 500 milliseconds to simulate time taken by a real task (like database access or server response).

4. Creating Threads

Thread thread1 = new Thread(user1);
Thread thread2 = new Thread(user2);
  • We wrap our Runnable object inside a Thread object.
  • This is required because only Thread has the start() method.

5. Starting Threads

thread1.start();
thread2.start();
  • Starts both threads.
  • Each thread will call the run() method of its corresponding Runnable object.

How Runnable Works Internally

1. You create a class that implements Runnable

  • This class defines the task you want to run in a separate thread.
  • It must override the run() method from the Runnable interface.
class MyTask implements Runnable {
    public void run() {
        System.out.println("Task running...");
    }
}

2. You override the run() method

  • This method contains the actual logic that will run when the thread starts.
  • No return value, no parameters.

3. You create a Thread object and pass the Runnable instance to it

  • The Thread constructor accepts a Runnable object.
  • This binds the task to the thread.
MyTask task = new MyTask();
Thread thread = new Thread(task);

4. You call start() on the Thread object

  • This creates a new OS-level thread and then calls the thread’s internal run() method in the background.

thread.start(); // starts new thread, runs task.run()

5. Internally, start() triggers run() on a new thread

  • start() sets up a new call stack and executes Runnable.run() from there.
  • If you call run() directly (without start()), the code runs in the main thread, no new thread is created.

Summary Flow:

You → implement Runnable → override run() → pass to Thread → call start() →
JVM → creates a new thread → calls run() in that thread

Java 8 Runnable with Lambda

Since Runnable is a functional interface, you can use lambda expressions (Java 8+) for more concise code:

public class LambdaRunnable {
    public static void main(String[] args) {
        Runnable task = () -> {
            for (int i = 1; i <= 3; i++) {
                System.out.println("Lambda Thread: " + i);
            }
        };

        Thread thread = new Thread(task);
        thread.start();
    }
}

Output

Lambda Thread: 1
Lambda Thread: 2
Lambda Thread: 3

Explanation:

1. Runnable is a functional interface

      • A functional interface has only one abstract method (run() in this case).
      • Java 8 allows you to use lambda expressions to write such interfaces more concisely.

      2. Using Lambda Expression:

      Runnable task = () -> {
          // code inside run() method
      };
      • This replaces the need to create a separate class that implements Runnable.
      • It’s a shortcut way of saying:
        “Run this block of code in a separate thread.”

      3. Creating and Starting the Thread:

      Thread thread = new Thread(task);
      thread.start();
      • A new Thread is created with the lambda Runnable task.
      • When start() is called, it runs the code inside the lambda in a new thread.

      Benefits of Using Lambda with Runnable:

      • Less code: No need to create a separate class.
      • Cleaner and readable.
      • Modern style using Java 8+ features.

      Advantages of Using Runnable

      1. Better resource sharing: Multiple threads can share the same Runnable object.
      2. Promotes clean code: Keeps your class hierarchy clean by allowing you to extend other classes.
      3. Functional Interface: Works seamlessly with lambda expressions.
      4. Separation of concerns: Logic of the task is separate from the thread management.