Two Ways to Create Threads in Java
Fast recall
Quick revision
Core answer
run(), or implement the `Runnable` interface and pass it to a Thread constructor. Runnable is preferred because it leaves your class free to extend something else and works with thread pools. A third, modern option is Callable, used when the task must return a value.Understanding of fundamental thread creation mechanisms and when to use each approach in production code.
Thinking extending Thread is the preferred approach, when composition via Runnable is better because Java doesn't support multiple inheritance.
Immediately state that Runnable is preferred in production because it separates the task from the thread execution mechanism and allows the class to extend other classes.
Interview-ready guide
Interview answer
There are two main ways to create a thread in Java.
First, you can extend the Thread class and override the run method with your task logic. Second, you can implement the Runnable interface and pass that to a Thread constructor. The Runnable approach is preferred in production code because it uses composition instead of inheritance.
That's important because Java doesn't support multiple inheritance, so if you extend Thread, you can't extend any other class like a business domain object or service base class. When you implement Runnable, your class is free to extend something else while still being executable in a thread.
In a real Spring Boot service handling API requests, you'd implement Runnable for background tasks so they can extend framework classes and also work with ExecutorService thread pools. The Thread approach is really only used when you need to customize thread behavior itself, which is rare. One common mistake is extending Thread first, then realizing you need to extend another class and having to refactor everything.
Always start with Runnable unless you have a specific reason to extend Thread.
Two Ways to Create Threads in Java
Direct Answer
There are two classic ways: extend the Thread class and override run(), or implement the Runnable interface and pass it to a Thread constructor. Runnable is preferred because it leaves your class free to extend something else and works with thread pools. A third, modern option is Callable, used when the task must return a value.
Code Example
// Approach 1: Extending Thread (less preferred)
class DownloadThread extends Thread {
private final String url;
DownloadThread(String url) {
this.url = url;
}
@Override
public void run() {
System.out.println("Downloading from " + url);
}
}
// Approach 2: Implementing Runnable (preferred)
class DownloadTask implements Runnable {
private final String url;
DownloadTask(String url) {
this.url = url;
}
@Override
public void run() {
System.out.println("Downloading from " + url);
}
}
// Usage comparison
var thread1 = new DownloadThread("https://api.example.com/data");
thread1.start(); // Extends Thread, call start() directly
var task = new DownloadTask("https://api.example.com/data");
var thread2 = new Thread(task); // Runnable needs a Thread wrapper
thread2.start();
Common Mistakes & Pitfalls
- Extending
Threadwhen composition is needed: Freshers extendThreadfirst, then realize they also need to extend another class and have to rewrite. The code compiles but locks you into a design corner. - Forgetting the
new Thread(...)wrapper: WithRunnable, you still have to wrap it in aThreadand callstart(). TheRunnabledoes not start on its own. - Reusing a
Threadinstance: Callingstart()again on a finishedThreadthrowsIllegalThreadStateException. EachThreadcan be started only once.
Check your understanding
Answer these without looking back at the article.
- What happens if you call run() instead of start()?
- Why is implementing Runnable preferred over extending Thread?
- What is Callable and how is it different from Runnable?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.