CodeToConcept
About

java

java Tutorials

Introduction
Java Variables
Java Data Types
Java Type Casting
Java Operators
Java Input/Output
Java Class & Object
java Method
Java Constructor
Java this keyword
Java super keyword
Java Method Overloading and Overriding
Java Interface
Java Abstract Class
Java Memory Management
Design Patterns
JDBC (Database Connectivity)
Java IDEs

java Tutorials

Introduction
Java Variables
Java Data Types
Java Type Casting
Java Operators
Java Input/Output
Java Class & Object
java Method
Java Constructor
Java this keyword
Java super keyword
Java Method Overloading and Overriding
Java Interface
Java Abstract Class
Java Memory Management
Design Patterns
JDBC (Database Connectivity)
Java IDEs

Sample Question

Think it through, then check the tutorial.

CodeToConcept

Quickly learn through simple, structured, and practical coding tutorials. Build your confidence from step zero to real-world experience.

© 2026 CodeToConcept. All rights reserved.

Quick Links

AboutTutorials

Tutorials

JavaHTMLCSS

Support

Privacy PolicyTerms of ServiceContact
GitHubTwitterYouTube

Java Runnable Interface

Runnable describes a task through one run() method. It does not create or start a thread by itself. We can hand the task to a Thread or an executor and keep the task logic separate from the way it is scheduled.

What is the Runnable Interface?

Runnable is a functional interface that represents a task that can be executed by a thread.

It contains a single abstract method:

public interface Runnable {
    void run();
}

The run() method contains the code that should be executed by the thread.

However, calling run() directly does not create a new thread. To execute the task on a separate thread, we create a Thread object and call its start() method.

Why Use Runnable?

Java provides two common ways to create a thread:

  • Extend the Thread class
  • Implement the Runnable interface

Using Runnable is often preferred when the class represents a task rather than being a specialized type of thread.

It also allows the class to extend another class because Java does not support multiple class inheritance.

Creating a Thread Using Runnable

There are two main steps:

  • Implement the Runnable interface.
  • Pass the Runnable object to a Thread object and call start().

Example

class MyTask implements Runnable {
 
    @Override
    public void run() {
        System.out.println("Task is running...");
    }
}
 
public class HelloWorld {
    public static void main(String[] args) {
 
        MyTask task = new MyTask();
 
        Thread thread = new Thread(task);
 
        thread.start();
    }
}

Output

Task is running...

The run() method contains the task, while the Thread object is responsible for executing that task on a separate thread.

How Runnable Works

Consider this statement:

Thread thread = new Thread(task);

Here:

  • task is an object of a class implementing Runnable.
  • The Thread object receives that task.
  • Calling start() starts a new thread.
  • The new thread eventually invokes the run() method.

The flow can be represented as:

Runnable object
      ↓
   Thread
      ↓
   start()
      ↓
New thread is created
      ↓
   run() executes

run() vs start()

This is one of the most important concepts when working with threads.

Calling run() directly:

task.run();

This is simply a normal method call.

It does not create a new thread.

Calling start():

thread.start();

This requests the JVM to start a new thread, and that thread executes the run() method.

Example

class MyTask implements Runnable {
 
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}
 
public class HelloWorld {
    public static void main(String[] args) {
 
        MyTask task = new MyTask();
 
        task.run(); // normal method call
 
        Thread thread = new Thread(task);
        thread.start(); // starts a new thread
    }
}

The first call executes on the current thread, while the second executes through a newly started thread.

Runnable with Lambda Expression

Runnable is a functional interface because it contains only one abstract method.

Therefore, we can use a lambda expression to create a Runnable.

Example

Runnable task = () -> {
    System.out.println("Task is running...");
};
 
Thread thread = new Thread(task);
 
thread.start();

This is a shorter alternative to creating a separate class.

Runnable with Lambda Expression Directly

We can also pass the lambda expression directly to the Thread constructor.

Thread thread = new Thread(() -> {
    System.out.println("Task is running...");
});
 
thread.start();

This approach is commonly used when the task is small and does not need to be reused.

Runnable Using Anonymous Class

Before lambda expressions were introduced in Java 8, an anonymous class was commonly used.

Runnable task = new Runnable() {
 
    @Override
    public void run() {
        System.out.println("Task is running...");
    }
};
 
Thread thread = new Thread(task);
 
thread.start();

Lambda expressions provide a more concise way to write the same type of task.

Runnable and Thread Class

There is an important difference between implementing Runnable and extending Thread.

Using Thread

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

Using Runnable

class MyTask implements Runnable {
 
    @Override
    public void run() {
        System.out.println("Task is running...");
    }
}

With Runnable, the task and the thread are separate concepts.

Runnable → represents the task
Thread   → executes the task

This separation is useful when designing multithreaded applications.

Runnable Allows Another Class to Be Extended

Java does not allow a class to extend two classes.

For example, this is not allowed:

class MyClass extends ClassA, Thread {
}

However, a class can extend another class and implement Runnable.

class MyTask extends Employee implements Runnable {
 
    @Override
    public void run() {
        System.out.println("Task is running...");
    }
}

This is one of the important advantages of using Runnable.

The class can inherit from another class while still defining a task that can be executed by a thread.

Running the Same Runnable with Multiple Threads

A single Runnable object can be passed to multiple Thread objects.

class MyTask implements Runnable {
 
    @Override
    public void run() {
        System.out.println(
            Thread.currentThread().getName() + " is running"
        );
    }
}
 
public class HelloWorld {
    public static void main(String[] args) {
 
        MyTask task = new MyTask();
 
        Thread thread1 = new Thread(task, "Thread-1");
        Thread thread2 = new Thread(task, "Thread-2");
 
        thread1.start();
        thread2.start();
    }
}

Both threads execute the same run() method.

The output order is not guaranteed because thread scheduling is controlled by the JVM and operating system.

Possible output:

Thread-1 is running
Thread-2 is running

The order may also be reversed.

Runnable Does Not Represent a Thread

It is important to understand that a Runnable object itself is not a thread.

For example:

Runnable task = () -> {
    System.out.println("Running task");
};

The Runnable represents the work to be performed.

A Thread is responsible for running that work:

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

So:

Runnable → What should be done
Thread   → Executes the work

Runnable and Thread Constructor

The Thread class provides constructors that can accept a Runnable.

For example:

Thread thread = new Thread(task);

We can also provide a thread name:

Thread thread = new Thread(task, "Worker-Thread");

Example:

Runnable task = () -> {
    System.out.println(
        Thread.currentThread().getName()
    );
};
 
Thread thread = new Thread(task, "Worker-Thread");
 
thread.start();

Output

Worker-Thread

Runnable with Thread Methods

Once a Runnable is passed to a Thread, we can use the methods provided by the Thread class.

For example:

Runnable task = () -> {
 
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
 
    System.out.println("Task completed");
};
 
Thread thread = new Thread(task);
 
thread.start();

Here:

  • Runnable defines the task.
  • Thread manages the execution.
  • sleep() temporarily pauses the executing thread.
  • start() begins thread execution.

Runnable vs Thread

FeatureRunnableThread
TypeInterfaceClass
Packagejava.langjava.lang
Main methodrun()run()
Creates a thread by itselfNostart() creates/starts the thread
Can extend another classYesNo, because it already extends Thread
RepresentsTask/workThread of execution
Lambda supportYesNot directly as a replacement for a Runnable task
Recommended for task definitionYesUsually separate task from execution

Runnable vs Callable

Runnable and Callable are both used to represent tasks, but they have an important difference.

Runnable

Runnable task = () -> {
    System.out.println("Task executed");
};

A Runnable:

  • does not return a result
  • cannot declare checked exceptions from run()
  • uses the run() method

Callable

Callable<Integer> task = () -> {
    return 100;
};

A Callable:

  • can return a result
  • can throw checked exceptions
  • uses the call() method

Callable is commonly used with ExecutorService and Future.

Runnable with ExecutorService

In modern Java applications, tasks are often submitted to an ExecutorService rather than creating threads manually.

Example:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
public class HelloWorld {
 
    public static void main(String[] args) {
 
        ExecutorService executor =
                Executors.newFixedThreadPool(2);
 
        Runnable task = () -> {
            System.out.println(
                Thread.currentThread().getName()
                + " is executing"
            );
        };
 
        executor.submit(task);
 
        executor.shutdown();
    }
}

Here:

Runnable
   ↓
ExecutorService
   ↓
Thread Pool
   ↓
Worker Thread
   ↓
run()

This approach is useful when an application needs to execute many tasks without manually creating a new thread for every task.

Important Points About Runnable

  • Runnable is an interface in java.lang.
  • It contains one abstract method: run().
  • Runnable is a functional interface.
  • It can be used with lambda expressions.
  • A Runnable represents a task, not a thread.
  • Calling run() directly does not start a new thread.
  • Calling Thread.start() starts a new thread that executes the task.
  • A class implementing Runnable can still extend another class.
  • The same Runnable object can be passed to multiple threads.
  • Runnable does not return a result.
  • Callable can be used when a task needs to return a result.
  • Runnable tasks can also be submitted to an ExecutorService.

Common Mistake: Calling run() Instead of start()

Incorrect when you want concurrent execution:

Runnable task = () -> {
    System.out.println("Task running");
};
 
Thread thread = new Thread(task);
 
thread.run();

Calling run() directly does not start a new thread.

Correct:

thread.start();

The key difference is:

run()   → normal method call
start() → starts a new thread

Common Mistake: Starting the Same Thread Twice

A Thread object can be started only once.

Thread thread = new Thread(task);
 
thread.start();
thread.start(); // Error

The second start() call results in an IllegalThreadStateException.

If the same task needs to be executed by multiple threads, create separate Thread objects:

Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
 
thread1.start();
thread2.start();

When Should You Use Runnable?

Runnable is a good choice when:

  • You want to separate a task from the thread that executes it.
  • Your class already extends another class.
  • You want to use lambda expressions for simple tasks.
  • The task does not need to return a result.
  • You want to submit tasks to an ExecutorService.

For modern applications, Runnable is commonly used together with executors and thread pools rather than creating large numbers of threads manually.

Key Takeaways

Runnable
   ↓
Represents a task
   ↓
Implement run()
   ↓
Pass Runnable to Thread
   ↓
Call start()
   ↓
Thread executes run()

The most important thing to remember is:

Runnable defines the work, while Thread provides the execution mechanism.

For example:

Runnable task = () -> {
    System.out.println("Hello from thread");
};
 
Thread thread = new Thread(task);
 
thread.start();

Here, Runnable defines what the thread should do, and Thread determines where and how that task is executed.

See you in next page....🙂

← Thread ClassThread Lifecycle →
← Thread ClassThread Lifecycle →