Skip to content
Mayank Pant
Go back

Ways of making threads in Java

There are two approaches to create threads in Java applications.

runnable-and-thread

It is important to note that the Thread class itself implements the Runnable interface. The Thread class provides the ability to create and manage a thread, along with several utility methods such as start(), sleep(), and join().

When we create a thread—regardless of whether we extend Thread or implement Runnable—the actual execution is always handled by a Thread object.


By extending Thread class

thread-class

Step 1: Create a Thread subclass

public class MyThread extends Thread {
	public void run() {
		for(int i = 0; i < 10; i++) {
			System.out.println("Hello Thread: " + i);
		}
	}
}

Step 2: Initiate and start the thread

public class Main {
	public static void main(String []args) {
		MyThread mt = new MyThread();
		mt.start();
		for(int i = 0; i < 10; i++) {
			System.out.println("Main Thread!");
		}
	}
}

Execution always starts from main() method and hence Main Thread is formed first and then goes through to generate other child Threads.

Extending the Thread class will lead to occupying the extends keyword and we will not be able to extend any other class, as Java does not allow multiple inheritance due to ambiguity in method names.


By implementing Runnable interface

runnable-interface

Step 1: Create a Runnable object

class MyRunnable implements Runnable {
	public void run() {
		System.out.println("code executed by thread: " + Thread.currentThread().getName());
	}
}

Step 2: Start the thread

public class Main() {
	public static void main(String[] args) {
		System.out.println("Going inside main method: " + Thread.currentThread().getName());
        MyRunnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);
        thread.start();
        System.out.println("Finished main method: " + Thread.currentThread().getName());
	}
}

To perform the 2nd step above, we can have the following cases:

Case-1: Just creating class object and starting it

MyRunnable mt = new MyRunnable();
mt.start();

Status: Compilation Error

Reason: start() method was not declared in MyRunnable class or in its superclass java.lang.Object, because start() method exists in java.lang.Thread class and Thread class object is needed to call that method.


Case-2: Directly calling the run method on object instance

MyRunnable mt = new MyRunnable();
mt.run();

Status: No Compilation Error

Main thread will access MyRunnable class run() method like a normal Java method, no multi-threading environment. Normal method call.


Case-3: Calling the thread class start() method without using the custom thread implementation

MyRunnable mt = new MyRunnable();
Thread t = new Thread();
t.start();

Status: No Compilation Error

start() method creates new thread and it access Thread class run() method, not MyRunnable class run() method. Because we did not pass the Runnable into the Thread constructor so how will it know to call the Runnable run() method.


Case-4: Correct

MyRunnable mt = new MyRunnable();
Thread t = new Thread(mt);
t.start();

Status: No Compilation Error

start() method creates new thread and it will bypass calling the Thread class run method and move on to call MyRunnable class run() method.

Explanation: explanation


Why do we have 2 ways to create threads?

Extending the Thread class was introduced early in Java (JDK 1.0) as a simple, direct way to create a thread.

What problem it solves

Limitations (why this is not ideal)

👉 This approach exists mainly for backward compatibility and simplicity

Implementing the Runnable interface exists because Java designers realized that: “A task is not the same thing as a thread.”

What problem it solves

👉 This approach supports composition over inheritance

Why Java didn’t remove Thread inheritance:

Java provides two ways to create threads because it separates thread execution (Thread) from task definition (Runnable). Extending Thread is a simple, legacy approach, while implementing Runnable follows better object-oriented design and is preferred in real-world applications.


Share this post on:

Previous Post
Introduction to Multithreading