Java Concurrency : Runnable
Tags: java, concurrency, multithreading
Published: 2023-09-10 07:29:18

## Brief Introduction

In Java, a [_Runnable_](https://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html) is an [_interface_](https://docs.oracle.com/javase/tutorial/java/concepts/interface.html) that represents a unit of work which can be executed by a [_thread_](https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html). It is part of the Java Concurrency framework `(java.util.concurrent)`.

## Example - A _Runnable_ Task

```
    public class Task implements Runnable {

        @Override
        public void run() {
            // do something meaningful
            System.out.println("Hello, World!");
        }
    }
```

## Example - A _Thread_ running a _Runnable_ Task
The Java Concurrency Framework provides a _Thread_ class which can be used for running a _Runnable_ in a separate thread of execution. This can be done by using the `start()` method provided by the _Thread_ class which initiates the execution of the thread's code in a new thread of control. This method is essential for multithreading because it tells the Java Virtual Machine (JVM) to begin executing the `run()` method of the underlying _Runnable_ concurrently in a separate thread.

```
    public static void main(String[] args) {
        System.out.println("Main method has started.");

        Thread t = new Thread(new Task("Charlie"));
        t.start();

        System.out.println("Main method has finished.");
    }

    private static class Task implements Runnable {

        private final String name;

        public Task(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            System.out.println("Task " + name + " has started.");
            System.out.println("Hello, World! This is Task : " + name);
            System.out.println("Task " + name + " has finished.");
        }
    }
```
The output of this program is non deterministic. This is because the operating system and the Java Virtual Machine (JVM) schedule and manage thread execution independently, and the order in which threads (in this case the main thread and the explicitly created task thread) are scheduled to run can vary each time the program is executed.

For instance, one possible output is

```
Main method has started.
Main method has finished.
Task Charlie has started.
Hello, World! This is Task : Charlie
Task Charlie has finished.
```
As we are storing the thread reference of the task thread in the main method, we can also wait for its completion before proceeding further in the main method. This can be done by using the `join()` method provided by the _Thread_ class which is a blocking call and blocks the caller thread until the callee thread completes execution and dies.

```
    public static void main(String[] args) throws InterruptedException {
        System.out.println("Main method has started.");

        Thread t = new Thread(new Task("Charlie"));
        t.start();
        t.join();

        System.out.println("Main method has finished.");
    }

    private static class Task implements Runnable {

        private final String name;

        public Task(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            System.out.println("Task " + name + " has started.");
            System.out.println("Hello, World! This is Task : " + name);
            System.out.println("Task " + name + " has finished.");
        }
    }
```
Note how we've also added the `InterruptedException` to the main method's signature. That is because the `join()` method of the _Thread_ class throws this checked exception. More about this in another post, but in summary When you call the join() method on a thread (let's call it "Thread 1") from another thread (let's call it "Thread 2"), Thread 2 will wait until Thread 1 completes its execution. In other words, Thread 2 will be blocked until Thread 1 finishes. If, while Thread 2 is waiting, another thread (let's call it "Thread 3") interrupts Thread 2 (e.g., by invoking Thread.interrupt() on Thread 2), Thread 2 will throw an InterruptedException.

The output of this program in this case is deterministic.

```
Main method has started.
Task Charlie has started.
Hello, World! This is Task : Charlie
Task Charlie has finished.
Main method has finished.
```

## Miscellaneous Example

```
    public static void main(String[] args) {
        System.out.println("Main method has started.");

        for (int i=0; i<5; ++i) {
            new Task();
        }

        System.out.println("Main method has finished.");
    }

    private static class Task extends Thread {

        private static int numTasks = 0;
        private final int id;

        public Task() {
            this.id = ++numTasks;
            this.start();
        }

        @Override
        public void run() {
            System.out.println("Task " + id + " has started.");

            try {
                TimeUnit.MILLISECONDS.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            System.out.println("Hello, World! This is Task : " + id);

            try {
                TimeUnit.MILLISECONDS.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            System.out.println("Task " + id + " has finished.");
        }
    }
```
The output for this is again non-deterministic. One possible output is

```
Main method has started.
Main method has finished.
Task 5 has started.
Task 2 has started.
Task 4 has started.
Task 1 has started.
Task 3 has started.
Hello, World! This is Task : 4
Hello, World! This is Task : 5
Hello, World! This is Task : 1
Hello, World! This is Task : 2
Hello, World! This is Task : 3
Task 2 has finished.
Task 4 has finished.
Task 5 has finished.
Task 1 has finished.
Task 3 has finished.
```