Multithreading
Creating Threads
Learn to subclass the Thread class vs implementing the Runnable interface with modern lambda expressions.
Interview: Frequently tests the architectural pros and cons of implementing Runnable vs extending Thread, including flexibility and object-oriented design.
Java provides two main entry points for defining custom execution paths: inheriting from the Thread class or implementing the Runnable interface. Modern Java developers heavily favor the functional Runnable approach.
Core Idea
Runnable defines the task to execute. Thread represents the worker that runs it. Separating task from executor is a critical best practice.
Why It Matters
Implementing Runnable allows your class to extend another parent class, whereas extending Thread consumes Java's single inheritance slot.
Interview Lens
Expect design questions comparing both approaches and asking you to write clean lambda-based thread initializations.
Runnable vs. Thread Class
Let's compare the two standard approaches:
1. Implementing Runnable (Preferred)
The task is defined separately in a class implementing Runnable or as a lambda expression.
- Supports multiple interface implementations.
- Allows inheritance from another base class.
- Fits naturally with the Executor framework and thread pools.
2. Extending Thread
The subclass overrides the run() method of the Thread class directly.
- Inflexible due to single inheritance.
- Tends to bundle the thread's lifecycle state with the task domain data, violating separation of concerns.
Code Walkthrough
Here is a code walkthrough showing both thread creation techniques:
// Approach 1: Subclassing Thread class CustomThread extends Thread { @Override public void run() { System.out.println("Running subclass thread: " + getName()); } }// Approach 2: Implementing Runnable class CustomRunnable implements Runnable { @Override public void run() { System.out.println("Running runnable thread: " + Thread.currentThread().getName()); } }
public class ThreadCreationDemo { public static void main(String[] args) { // Run Subclass CustomThread t1 = new CustomThread(); t1.start();
// Run Runnable Thread t2 = new Thread(new CustomRunnable()); t2.start();
// Run Lambda Runnable (Modern way) Thread t3 = new Thread(() -> System.out.println("Running Lambda: " + Thread.currentThread().getName())); t3.start(); } }
Interview-Relevant Information
Q: Can we run the same Thread object multiple times?
Answer: No, once a thread completes execution and exits its run method, it is terminated (Dead state). Calling start() on a thread object that has already started or finished will throw an IllegalThreadStateException at runtime.
Quick Checklist
Do you know why Runnable is preferred over Thread? Can you write a thread using lambdas? If yes, you are good to go.
Use Cases
Defining standalone background tasks that do not share state with other application services.
Asynchronous event dispatching to avoid locking the UI thread.
Common Mistakes
Instantiating a Runnable but forgetting to pass it to a Thread instance, which prevents execution.
Inheriting Thread class solely to run a basic function, cluttering object design.