Java Thread and Runnable Tutorial

This is the second part of my tutorial series on Java Concurrency. In the first part, we learned the basics of concurrency, processes and threads. In this post, we’ll learn how to create new threads and run tasks inside those threads.

Creating and Starting a Thread

There are two ways to create a thread in Java -

1. By extending Thread class

You can create a new thread simply by extending your class from Thread and overriding it’s run() method.

The run() method contains the code that is executed inside the new thread. Once a thread is created, you can start it by calling the start() method.

public class ThreadExample extends Thread {

    // run() method contains the code that is executed by the thread.
    @Override
    public void run() {
        System.out.println("Inside : " + Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        System.out.println("Inside : " + Thread.currentThread().getName());

        System.out.println("Creating thread...");
        Thread thread = new ThreadExample();

        System.out.println("Starting thread...");
        thread.start();
    }
}
# Output
Inside : main
Creating thread...
Starting thread...
Inside : Thread-0

Thread.currentThread() returns a reference to the thread that is currently executing. In the above example, I’ve used thread’s getName() method to print the name of the current thread.

Every thread has a name. you can create a thread with a custom name using Thread(String name) constructor. If no name is specified then a new name is automatically chosen for the thread.

2. By providing a Runnable object

Runnable interface is the primary template for any object that is intended to be executed by a thread. It defines a single method run(), which is meant to contain the code that is executed by the thread.

Any class whose instance needs to be executed by a thread should implement the Runnable interface.

The Thread class itself implements Runnable with an empty implementation of run() method.

For creating a new thread, create an instance of the class that implements Runnable interface and then pass that instance to Thread(Runnable target) constructor.

public class RunnableExample implements Runnable {

    public static void main(String[] args) {
        System.out.println("Inside : " + Thread.currentThread().getName());

        System.out.println("Creating Runnable...");
        Runnable runnable = new RunnableExample();

        System.out.println("Creating Thread...");
        Thread thread = new Thread(runnable);

        System.out.println("Starting Thread...");
        thread.start();
    }

    @Override
    public void run() {
        System.out.println("Inside : " + Thread.currentThread().getName());
    }
}
# Output
Inside : main
Creating Runnable...
Creating Thread...
Starting Thread...
Inside : Thread-0

Note that, instead of creating a class which implements Runnable and then instantiating that class to get the runnable object, you can create an anonymous runnable by using Java’s anonymous class syntax.

Anonymous classes enable you to make your code more concise. They enable you to declare and instantiate a class at the same time. - From Java doc.

public class RunnableExampleAnonymousClass {

    public static void main(String[] args) {
        System.out.println("Inside : " + Thread.currentThread().getName());

        System.out.println("Creating Runnable...");
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("Inside : " + Thread.currentThread().getName());
            }
        };

        System.out.println("Creating Thread...");
        Thread thread = new Thread(runnable);

        System.out.println("Starting Thread...");
        thread.start();
    }
}

The above example can be made even shorter by using Java 8’s lambda expression -

public class RunnableExampleLambdaExpression {

    public static void main(String[] args) {
        System.out.println("Inside : " + Thread.currentThread().getName());

        System.out.println("Creating Runnable...");
        Runnable runnable = () -> {
            System.out.println("Inside : " + Thread.currentThread().getName());
        };

        System.out.println("Creating Thread...");
        Thread thread = new Thread(runnable);

        System.out.println("Starting Thread...");
        thread.start();

    }
}

Runnable or Thread, Which one to use?

The first method, where you create a thread by extending from Thread class is very limited because once you extend your class from Thread, you cannot extend from any other class since Java doesn’t allow multiple inheritance.

Also, If you follow good design practice, Inheritance is meant for extending the functionality of the parent class, but when you create a thread, you don’t extend the functionality of Thread class, you merely provide the implementation of run() method.

So, In general, You should always use Runnable object to create a thread. This method is more flexible. It allows your class to extend from any other class. Also, you can use anonymous class syntax and Java 8’s lambda expression with Runnable to make your code more concise.

Pausing execution of a Thread using sleep()

The sleep() method provided by Thread class allows you to pause the execution of the currently executing thread for the specified number of milliseconds.

public class ThreadSleepExample {

    public static void main(String[] args) {
        System.out.println("Inside : " + Thread.currentThread().getName());

        String[] messages = {"If I can stop one heart from breaking,",
                "I shall not live in vain.",
                "If I can ease one life the aching,",
                "Or cool one pain,",
                "Or help one fainting robin",
                "Unto his nest again,",
                "I shall not live in vain"};

        Runnable runnable = () -> {
            System.out.println("Inside : " + Thread.currentThread().getName());

            for(String message: messages) {
                System.out.println(message);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    throw new IllegalStateException(e);
                }
            }
        };

        Thread thread = new Thread(runnable);

        thread.start();
    }
}
# Output
Inside : main
Inside : Thread-0
If I can stop one heart from breaking,
I shall not live in vain.
If I can ease one life the aching,
Or cool one pain,
Or help one fainting robin
Unto his nest again,
I shall not live in vain

The above example consists of a for loop which iterates over the messages array, prints the current message, waits for 2 seconds by calling Thread.sleep(), and then proceeds with the next iteration.

sleep() method throws InterruptedException if any thread interrupts the current thread. InterruptedException is a checked exception and it must be handled.

Waiting for completion of another thread using join()

The join() method allows one thread to wait for the completion of the other. In the following example, Thread 2 waits for the completion of Thread 1 for 1000 milliseconds by calling Thread.join(1000), and then starts the execution -

public class ThreadJoinExample {

    public static void main(String[] args) {
        // Create Thread 1
        Thread thread1 = new Thread(() -> {
            System.out.println("Entered Thread 1");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new IllegalStateException(e);
            }
            System.out.println("Exiting Thread 1");
        });

        // Create Thread 2
        Thread thread2 = new Thread(() -> {
            System.out.println("Entered Thread 2");
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                throw new IllegalStateException(e);
            }
            System.out.println("Exiting Thread 2");
        });

        System.out.println("Starting Thread 1");
        thread1.start();

        System.out.println("Waiting for Thread 1 to complete");
        try {
            thread1.join(1000);
        } catch (InterruptedException e) {
            throw new IllegalStateException(e);
        }

        System.out.println("Waited enough! Starting Thread 2 now");
        thread2.start();
    }
}
Starting Thread 1
Waiting for Thread 1 to complete
Entered Thread 1
Waited enough! Starting Thread 2 now
Entered Thread 2
Exiting Thread 1
Exiting Thread 2

The waiting time for Thread.join() is equal to MIN(time taken for the thread to terminate, number of milliseconds specified in the method argument).

The join() method can also be called without an argument. It this case, it simply waits until the thread dies.

Conclusion

In this tutorial, we learned two ways of creating threads in Java applications. We also learned about Thread’s sleep() and join() methods. All the code snippets used in this tutorial can be found in my github repository.

In the next blog post, we’ll learn how to manage threads in your application using Java’s executor framework.

Thank you for reading. Please ask any doubts or questions in the comment section below.