Skip to main content

Runnable Interface

  • The Runnable interface is a functional interface that represents a task to be executed by a thread.

  • It contains a single abstract method, run, which defines the code that constitutes the task.

Why Use the Runnable Interface?

  • The Runnable interface is preferred for creating threads because it allows a class to extend another class while still being able to execute tasks concurrently.

  • This approach offers more flexibility compared to extending the Thread class directly.

How to Implement the Runnable Interface

To create a task using the Runnable interface, follow these steps:

  • Create a Class that Implements Runnable: Define a class that implements the Runnable interface and override the run method.

  • Instantiate a Thread Object: Create an instance of the Thread class, passing the Runnable object to its constructor.

  • Start the Thread: Call the start method on the Thread object to execute the run method in a new thread.

Using Runnable:

Implementing the Runnable Interface:

Let's begin by creating a simple class that implements the Runnable interface.

Example:

 class PrintTask implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " - Task is running: " + i);
}
}
}

In this example, PrintTask is a class implementing Runnable. The run method contains the code to be executed concurrently.

Creating Threads with Runnable:

Now, let's use our PrintTask class to create threads that execute the defined task.

Example:

public class Main {
public static void main(String[] args) {
// Creating an instance of the class implementing Runnable
PrintTask printTask = new PrintTask();

// Creating two threads using the Runnable instance
Thread thread1 = new Thread(printTask);
Thread thread2 = new Thread(printTask);

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

In this example, two threads (thread1 and thread2) are created using the same instance of PrintTask. This demonstrates how a single Runnable instance can be used by multiple threads.

Output

Thread-1 - Task is running: 0
Thread-0 - Task is running: 0
Thread-0 - Task is running: 1
Thread-0 - Task is running: 2
Thread-0 - Task is running: 3
Thread-0 - Task is running: 4
Thread-1 - Task is running: 1
Thread-1 - Task is running: 2
Thread-1 - Task is running: 3
Thread-1 - Task is running: 4

Anonymous Runnable

What is an Anonymous Runnable?

  • An anonymous Runnable is a Runnable implementation created without a separate named class.

  • Instead, the implementation is provided directly within the code where it is needed.

  • This is especially useful for simple tasks that don't require a reusable class.

Example of Anonymous Runnable

Here’s an example that demonstrates how to use an anonymous Runnable to create and start threads:

public class Main {
public static void main(String[] args) {
// Creating threads using anonymous Runnable
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " - Task is running: " + i);
}
}
});

Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " - Task is running: " + i);
}
}
});

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

Explanation

  1. Creating Threads with Anonymous Runnable:

    • We create two Thread objects, thread1 and thread2, each initialized with an anonymous Runnable.

    • The Runnable implementation is provided directly within the new Runnable() { ... } block.

  2. Override the run Method:

    • Each Runnable implementation overrides the run method to specify the task to be executed by the thread.

    • Inside the run method, a loop prints the current thread's name and the iteration index.

  3. Starting the Threads:

    • We call the start method on both thread1 and thread2 to begin execution of their respective run methods in separate threads.

Expected Output

The output will show both threads executing their tasks concurrently. The exact order of the output may vary due to the nature of concurrent execution, but it will look something like this:

Thread-0 - Task is running: 0
Thread-1 - Task is running: 0
Thread-0 - Task is running: 1
Thread-1 - Task is running: 1
Thread-0 - Task is running: 2
Thread-1 - Task is running: 2
Thread-0 - Task is running: 3
Thread-1 - Task is running: 3
Thread-0 - Task is running: 4
Thread-1 - Task is running: 4

Lambda Expression for Runnable:

With the introduction of lambda expressions in Java 8, the syntax for implementing Runnable becomes even more concise.

Example:

public class Main {
public static void main(String[] args) {
// Creating threads using lambda expression for Runnable
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " - Task is running: " + i);
}
});

Thread thread2 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " - Task is running: " + i);
}
});

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

The use of lambda expressions makes the code concise and expressive, especially for short Runnable implementations.

Real-Life Example: Download Manager

Let's consider a real-world scenario where Runnable can be applied. Imagine a simple download manager that needs to download multiple files concurrently.

Example Code:

import java.util.List;

public class DownloadManager {
public static void main(String[] args) {
List<String> filesToDownload = List.of("File1", "File2", "File3", "File4", "File5");

for (String file : filesToDownload) {
// Creating a thread for each download task using lambda expression
Thread downloadThread = new Thread(() -> {
System.out.println("Downloading " + file + " on thread: " + Thread.currentThread().getName());
// Simulate download process
try {
Thread.sleep(2000); // Simulating download time
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Download completed for " + file);
});
downloadThread.start();
}
}
}

Explanation

  1. List of Files:

    • We create a list of file names to be downloaded using List.of("File1", "File2", "File3", "File4", "File5").
  2. Creating and Starting Threads:

    • We loop through each file in the list.

    • For each file, we create a new Thread object using a lambda expression to implement the Runnable interface.

    • Inside the lambda expression, we print a message indicating the start of the download.

    • We simulate the download process using Thread.sleep(2000), which pauses the thread for 2 seconds.

    • After the pause, we print a message indicating the download completion.

    • We start the thread by calling the start method.

Expected Output:

The output may vary due to the concurrent nature of threads, but a sample output might look like:

Downloading File1 on thread: Thread-0
Downloading File2 on thread: Thread-1
Downloading File3 on thread: Thread-2
Downloading File4 on thread: Thread-3
Downloading File5 on thread: Thread-4
Download completed for File1
Download completed for File2
Download completed for File3
Download completed for File4
Download completed for File5

Please note that the exact order of thread execution may vary, as threads run concurrently.

Summary

  • Runnable Interface: Represents a task to be executed by a thread. Contains a single abstract method, run.

  • Why Use Runnable: Allows a class to extend another class while still being able to execute tasks concurrently. Offers more flexibility compared to extending the Thread class directly.

  • Implementing Runnable:

    • Create a class that implements Runnable.

    • Override the run method to define the task.

    • Create a Thread object, passing the Runnable instance to the constructor.

    • Start the thread using the start method.

  • Anonymous Runnable: An anonymous Runnable is created without a separate named class. Useful for simple, short-lived tasks. Implementation is provided directly within the code block.

  • Lambda Expression for Runnable: Introduced in Java 8, makes Runnable implementation more concise. Enhances code readability and expressiveness.

  • Real-Life Example - Download Manager: Demonstrates concurrent downloads using Runnable. Each file download runs in its own thread, improving efficiency and responsiveness.

Understanding and using the Runnable interface is crucial for writing concurrent, clean, and efficient Java code.