← Back to Multithreading Course

JAVA MULTITHREADING COURSE · CLASS 03

The Runnable Interface

In our previous class, we created a thread by extending Java's Thread class.

Today, we will learn another way of creating threads using the Runnable interface.

1. A Quick Recap

In Class 2, we created our own class by extending Java's Thread class.

Remember this example?

public class MyFirstThread extends Thread {

    @Override
    public void run() {
        System.out.println("Hello from the new thread!");
    }

    public static void main(String[] args) {

        MyFirstThread thread = new MyFirstThread();

        thread.start();
    }
}

We extended the Thread class and wrote our task inside the run() method.

Then we called start() to start the new thread.

This approach works perfectly well. But Java gives us another approach too.

2. What Is the Runnable Interface?

Java provides an interface called Runnable.

In simple words, Runnable represents a task that we want a thread to execute.

Think about our worker example again.

A worker is the person who performs the work. The task is the actual work assigned to that person.

Similarly, in Java:

This gives us a useful separation between the work and the thread performing the work.

3. Our First Runnable Example

Let's create the same example that we created in the previous class, but this time using Runnable.

public class MyTask implements Runnable {

    @Override
    public void run() {
        System.out.println("Hello from the new thread!");
    }

    public static void main(String[] args) {

        MyTask task = new MyTask();

        Thread thread = new Thread(task);

        thread.start();
    }
}

Don't worry if this looks slightly different. Let's understand it step by step.

4. Understanding the Code

Step 1: Implement the Runnable Interface

public class MyTask implements Runnable

Notice that we are using implements instead of extends.

This is because Runnable is an interface.

Our class MyTask now represents a task that can be executed by a thread.

Step 2: Implement the run() Method

@Override
public void run() {
    System.out.println("Hello from the new thread!");
}

Just like our previous example, we write the work inside the run() method.

Since Runnable requires us to implement the run() method, we provide its implementation here.

Step 3: Create an Object of MyTask

MyTask task = new MyTask();

We create an object of our MyTask class.

Remember, this object represents the task. We have not created or started a new thread yet.

Step 4: Pass the Task to a Thread

Thread thread = new Thread(task);

This is the important new step.

We create an object of Java's Thread class and pass our task object to it.

In simple words, we are telling the thread: "Here is the task I want you to perform."

Step 5: Start the Thread

thread.start();

Finally, we call start().

Java starts a new thread, and that thread executes the run() method of our MyTask object.

5. Why Do We Need Runnable?

You might be wondering: if we can create threads by extending the Thread class, why do we need Runnable?

Let's understand one important reason.

In Java, a class can extend only one class.

Imagine that our class already extends another class because our application needs it.

We cannot extend the Thread class as well.

But we can still implement the Runnable interface.

This allows us to define our task using Runnable without extending the Thread class.

Remember

Extending Thread is one way to create a thread. Implementing Runnable is another way.

Runnable also allows us to keep our task separate from the Thread object that executes it.

6. Thread vs Runnable: Let's Compare

Both approaches can be used to execute work on a separate thread.

Let's look at their basic differences.

Approach 1: Extending Thread

public class MyTask extends Thread {

    @Override
    public void run() {
        System.out.println("Performing a task");
    }
}

Here, our class itself extends Thread.

Approach 2: Implementing Runnable

public class MyTask implements Runnable {

    @Override
    public void run() {
        System.out.println("Performing a task");
    }
}

Here, our class represents the task. We pass its object to a Thread object separately.

Let's remember the difference:

Both approaches use the start() method on a Thread object to begin execution on a new thread.

7. Try It Yourself

Let's make a small change to our Runnable example.

Replace the message inside the run() method:

@Override
public void run() {
    System.out.println("I am performing a background task!");
}

Now change the main method to:

public static void main(String[] args) {

    MyTask task = new MyTask();

    Thread thread = new Thread(task);

    thread.start();

    System.out.println("Main thread is doing other work!");
}

Run the program and observe the output.

Can you identify which message comes from the new thread and which one comes from the main thread?

8. Test Your Understanding

Let's check what we have learned. Choose one answer for each question.

Question 1: What is Runnable in Java?

Question 2: Which keyword is used when implementing Runnable?

Question 3: Where do we write the task when implementing Runnable?

Question 4: What is the purpose of this statement?

Thread thread = new Thread(task);

Question 5: Can a Java class extend another class and implement Runnable at the same time?

9. What Did We Learn Today?

Let's quickly revise:

Excellent! We now know two ways of creating threads in Java.

Keep practising these examples. They will help us understand more advanced concepts later.

Coming Up in Class 4

We have learned how to create threads. But what happens to a thread after it starts?

In our next class, we will understand the Thread Lifecycle and the different states a thread can go through.

Your Course Progress

0% Completed

You have completed 3 out of 44 classes.