Creating Threads in Java
Fast recall
Quick revision
Core answer
Whether you can name the two ways and explain why implementing Runnable (and using an ExecutorService) is preferred over extending Thread.
Extending Thread out of habit, or creating threads with new Thread().start() directly instead of using an ExecutorService.
Two ways: extend Thread or implement Runnable; prefer Runnable + ExecutorService (decouples task from execution, enables pools). Don't create threads directly in production.
Interview-ready guide
Interview answer
Java has two main ways to create a thread.
You can extend the Thread class and override its run method, then call start. Or you can implement Runnable — a task — and pass it to a Thread and call start.
Implementing Runnable is preferred because it separates the task from how it runs, so you can pass the same Runnable to a thread pool, and it doesn't consume your one superclass slot, where extending Thread ties your class to a single execution and blocks subclassing anything else.
In production code you rarely create threads directly; you submit Runnables or Callables to an ExecutorService, which manages a reusable pool, and Java's modern Virtual Threads from Project Loom offer lightweight concurrency for I/O-heavy tasks. A classic mistake is calling run instead of start — run executes on the current thread, not a new one, which defeats the point entirely.
Creating Threads in Java
Direct Answer
Java has two main ways to create a thread: extend Thread and override run(), or implement Runnable and pass it to a Thread (or an ExecutorService). Implementing Runnable is preferred because it separates the task from the execution and lets you reuse thread pools; extending Thread ties your class to a single execution and blocks subclassing anything else. In practice you rarely create threads directly — you submit Runnables/Callables to an ExecutorService, which manages a pool for you. There's also the modern Virtual Threads (Project Loom) approach for lightweight concurrency.
Code Example
// (1) EXTEND Thread (works, but ties you to a single execution).
class MyThread extends Thread {
public void run() { System.out.println("running on " + getName()); }
}
new MyThread().start();
// (2) IMPLEMENT Runnable (PREFERRED: task separate from execution).
Runnable task = () -> System.out.println("running on " + Thread.currentThread());
new Thread(task).start();
// (3) PRACTICE: use an ExecutorService (pools threads, don't create directly).
ExecutorService pool = Executors.newFixedThreadPool(4);
pool.submit(task); // reuse a pooled thread
pool.submit(task);
pool.shutdown(); // always shut down
// (4) MODERN: virtual threads (Project Loom) for lightweight I/O concurrency.
Thread.startVirtualThread(task); // millions of cheap threads
Common Mistakes & Pitfalls
- Calling run() instead of start() — run() runs on the current thread, not a new one.
- Creating threads with new Thread().start() directly instead of using an ExecutorService pool.
- Extending Thread when you need to subclass something else (you've used your one superclass).
Check your understanding
Answer these without looking back at the article.
- What are the two main ways to create a thread in Java?
- Why is implementing Runnable preferred over extending Thread?
- What's the difference between calling start() and run()?
- What should you use in production instead of creating threads directly (ExecutorService)?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.