Java : Runnable Interface
The Runnable Interface in Java: A Detailed Guide with Examples
In Java, multithreading allows programs to perform multiple tasks concurrently. The Runnable interface plays a vital role in Java's thread management system, enabling developers to create and manage threads in a more flexible and efficient way.
In this blog post, we will dive deep into the Runnable interface in Java, its purpose, how to use it, and an example to demonstrate its practical use.
What is the Runnable Interface in Java?
The Runnable interface represents a task that can be executed concurrently by a thread. Unlike extending the Thread class, which limits your class to only inheriting from Thread, implementing Runnable allows your class to implement multiple interfaces. This provides more flexibility and is considered the preferred way to define tasks that need to be executed by multiple threads.
Get Rupam Thakre’s stories in your inbox
Join Medium for free to get updates from this writer.
The Runnable interface has a single method:
public interface Runnable {
void run();
}The run() method contains the code that should be executed when the thread is started. When a thread is created using the Runnable interface, the run() method will be invoked by the thread when it is started.
Why Use Runnable Over Thread?
- Flexibility: By using the
Runnableinterface, you can still extend other classes (since Java supports single inheritance), while withThread, you can only extendThread. - Separation of Concerns: The
Runnableinterface separates the task (logic) from the thread itself, which makes it easier to maintain and modify. - Better Resource Management: Using the
Runnableinterface allows you to reuse the same instance of a task in different threads, as opposed to extendingThread, which requires creating a new subclass for each task.
How to Use the Runnable Interface in Java
The Runnable interface is implemented in two simple steps:
- Create a class that implements
Runnable. - Pass the
Runnableobject to aThread.
Here’s how it works in detail:
- Create a class that implements the
Runnableinterface and provides the implementation of therun()method. - Create a
Threadobject and pass theRunnableinstance to it. - Start the thread using the
start()method, which internally calls therun()method.
Example 1: Simple Runnable Example
Here’s a basic example that demonstrates how to use the Runnable interface to execute a task in a separate thread.
Code Example:
class MyRunnable implements Runnable {
@Override
public void run() {
// Task to be executed by the thread
System.out.println("Thread is running using Runnable!");
}
public static void main(String[] args) {
// Create a Runnable instance
MyRunnable myRunnable = new MyRunnable();
// Create a Thread and pass the Runnable instance
Thread thread = new Thread(myRunnable);
// Start the thread
thread.start();
System.out.println("Main thread is running!");
}
}Explanation:
- The class
MyRunnableimplements theRunnableinterface and overrides therun()method to define the task the thread will perform. - We then create a
Threadobject and pass theMyRunnableinstance to it. - Calling
start()initiates the execution of therun()method in a new thread, and the task is executed concurrently with the main thread.
Output:
Thread is running using Runnable!
Main thread is running!Note: The order of the output may vary because threads run concurrently.
Example 2: Using Multiple Threads with Runnable
Let’s extend our example to create multiple threads, each executing a task concurrently.
Code Example:
class Task implements Runnable {
private String taskName;
public Task(String taskName) {
this.taskName = taskName;
}
@Override
public void run() {
System.out.println("Task " + taskName + " is being executed by " + Thread.currentThread().getName());
}
public static void main(String[] args) {
// Create multiple Runnable tasks
Task task1 = new Task("Task 1");
Task task2 = new Task("Task 2");
Task task3 = new Task("Task 3");
// Create multiple threads
Thread thread1 = new Thread(task1);
Thread thread2 = new Thread(task2);
Thread thread3 = new Thread(task3);
// Start the threads
thread1.start();
thread2.start();
thread3.start();
}
}Explanation:
- We created a class
Taskthat takes a task name as a parameter and prints which task is being executed by which thread. - Multiple threads (
thread1,thread2, andthread3) are created, each executing a differentTask. - Each thread will print out its name and the task it is running.
Output:
Task Task 1 is being executed by Thread-0
Task Task 2 is being executed by Thread-1
Task Task 3 is being executed by Thread-2The output order can vary since the threads run concurrently and the thread scheduler decides which thread runs at any given time.
Using Runnable with ExecutorService
While using the Thread class directly works fine, it’s often more efficient to use ExecutorService to manage thread execution in real-world applications. The ExecutorService manages a pool of threads, improving performance and resource management.
Code Example: Using ExecutorService with Runnable
import java.util.concurrent.*;
class Task implements Runnable {
private String taskName;
public Task(String taskName) {
this.taskName = taskName;
}
@Override
public void run() {
System.out.println("Executing " + taskName + " by " + Thread.currentThread().getName());
}
public static void main(String[] args) {
// Create a fixed thread pool with 3 threads
ExecutorService executor = Executors.newFixedThreadPool(3);
// Submit tasks to the executor
executor.submit(new Task("Task 1"));
executor.submit(new Task("Task 2"));
executor.submit(new Task("Task 3"));
// Shutdown the executor
executor.shutdown();
}
}Explanation:
- We use the
ExecutorServicewith a fixed thread pool size of 3. This means at most 3 threads will run concurrently. - We submit three tasks to the executor, and it assigns them to the available threads in the pool.
- The
shutdown()method is called to clean up resources after the tasks are completed.
Output:
Executing Task 1 by pool-1-thread-1
Executing Task 2 by pool-1-thread-2
Executing Task 3 by pool-1-thread-3The ExecutorService handles thread creation, task scheduling, and resource management efficiently.
Advantages of Using Runnable
- Decouples Task and Thread Management: By using
Runnable, you separate the logic (task) from the thread management, making your code more flexible and easier to maintain. - Better Resource Management: You can use
ExecutorServiceto manage a pool of threads, avoiding the overhead of creating new threads each time. - Supports Multiple Threads: A single
Runnableinstance can be passed to multiple threads, allowing the same task to be executed by different threads concurrently.
Common Use Cases for Runnable
- Concurrent Tasks in Applications: When you need to execute multiple tasks concurrently, such as handling requests in a server or processing large data sets in parallel.
- UI Thread Handling: In GUI applications (e.g., Swing),
Runnableis commonly used to offload long-running tasks to worker threads without blocking the UI. - Background Processes: For background tasks like file I/O or network calls, using
Runnablelets you keep the main thread responsive.
Conclusion
The Runnable interface in Java is a powerful tool for creating multithreaded applications. It allows you to define tasks and pass them to threads for concurrent execution, offering a cleaner and more flexible alternative to directly extending the Thread class. Whether you're working with basic threads or using ExecutorService for efficient thread management, the Runnable interface is an essential building block for any Java multithreading application.
By understanding how to implement and utilize the Runnable interface, you can create responsive, efficient, and scalable applications that make full use of modern multi-core processors.






