How to Use the Thread and Runnable Classes


1. Introduction

In this article, we'll learn how to initialize a new thread in Java using the Runnable and Thread classes.

2. Threads and Tasks

We'll not dive into the fundamentals of concurrency and threads in this post. However, it is easier to talk about how to create threads if you have a brief idea about what they are. In this section, we'll see some concepts regarding concurrency and threads.

First, let's define what a task is in terms of concurrency. A task is a single unit of work executed by a thread at a given time. When a thread finishes a task, it becomes available to run another task. That process keeps going until there are no tasks left.

2.1. Threads in Java

When you define a class with a main method, the JVM starts a new thread for that method execution: the main thread. We can create new threads not associated with the main thread to run in parallel. The primary purpose of creating new threads is to introduce concurrency or parallelism. The Thread class defines a thread in Java. We'll see how to use it in short.

2.2. Tasks Using Runnable vs. Callable

The Runnable and Callable interfaces represent a task in Java. Those two interfaces do the same, with some nuances:

  • Runnable returns void after execution. On the other hand, Callable returns a generic type.
  • Runnable doesn't throw exceptions. Callable throws a checked exception.

In short, we'll see how to define tasks using the Runnable interface.

3. Create Tasks in Java

3.1. Create a Task Implementing Runnable

To create a task, we can implement the Runnable interface and instantiate it in the caller code. Let's first define a class Printer to implement Runnable and override the run method:

1public class Printer implements Runnable {
2  @Override
3  public void run() {   
4    for (int i = 0; i < 10; i++) {
5      System.out.println("count=" + i);
6    }
7  }

The run method returns void and receives zero arguments. In the example, the method prints the numbers from 0 to 9.

2.4. Create a Task using Lambda

The good news is that Runnable is a Functional Interface containing only one abstract method called run. Therefore, we can substitute the Printer class implementation with just a lambda expression. Let's rewrite our run method using a lambda expression:

1Runnable printer = () -> {
2    for (int i = 0; i < 10; i++) {
3        System.out.println("count=" + i);
4    }

The value after = is a lambda expression that creates a custom implementation with no name of the Runnable interface and overrides the run method with the content inside brackets.

4. Create Threads in Java

4.1. Create Threads using the Thread Constructor

After defining a task using Runnable, we can create threads to execute them. Let's use the Thread class constructor with the Printer class as an argument:

1public class ThreadExample {
3  public static void main(String[] args) {
4    new Thread(new Printer()).start();
5  }

The constructor receives a Runnable and creates an idle thread. Then, the start method executes the run method of the Printer class in parallel with the main thread.

Another way to achieve the same without an instance of Printer, using a lambda expression, is the following:

1new Thread(() -> {
2    for (int i = 0; i < 10; i++) {
3        System.out.println("count=" + i);
4    }

For simplicity, we'll not look at the Callable interface since it does the same thing as Runnable, except for the differences mentioned in section 2.2.

4.2. Create Threads by Extending the Thread Class

Instead of defining a task with Runnable, we can extend the Thread class and override the run method to create a parallel thread. To achieve that, let's use the following code snippet:

 3public class ThreadExample extends Thread {
 5  @Override
 6  public void run() {
 7    for (int i = 0; i < 10; i++) {
 8      System.out.println("count=" + i);
 9    }
10  }
12  public static void main(String[] args) {
13    new ThreadExample().start();
14  }

The ThreadExample class extends the run method and implements a different behavior. We can call the start method to create a thread from that class instance. At the main method, we can create an instance of ThreadExample and call start from it to generate a parallel thread.

5. Conclusion

In this article, we've learned a simplistic way to create threads in Java using the Thread and Runnable classes.