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:
- Runnable: Represents the task.
- Thread: Executes that task.
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:
- Extending Thread: Our class is a Thread.
- Implementing Runnable: Our class represents a task that a Thread can execute.
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:
- Runnable is a Java interface.
- We implement Runnable to define a task.
- We write the task inside the run() method.
- We pass the Runnable object to a Thread object.
- We call start() on the Thread object to execute the task on a new thread.
- Runnable separates the task from the thread that executes it.
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.