1. The Hook (The "Byte-Sized" Intro)
In a Nutshell: Three ways to create threads: (1) Extend Thread class (simple but limits inheritance), (2) Implement Runnable interface (preferred—separates task from thread), (3) Use Callable<V> (returns value, throws exceptions). start() creates new thread and calls run(). Calling run() directly executes in current thread (no concurrency!)Java 8+ allows lambda expressions for Runnable/Callable.
Think of hiring employees. Thread class = hiring full-time employee (comes with overhead). Runnable = hiring contractor (flexible, reusable). Callable = contractor who delivers results!
2. Conceptual Clarity (The "Simple" Tier)
💡 The Analogy
- Thread class: Buy a car (comes with engine built-in)
- Runnable: Buy engine separately, install in any car (flexibility)
- Callable: Engine with odometer (returns mileage data)
3. Technical Mastery
Method 1: Extending Thread Class
java
class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread running: " + getName());
}
}
// Usage
MyThread t = new MyThread();
t.start(); // ✅ Creates new thread
// t.run(); // ❌ Runs in current thread!Method 2: Implementing Runnable (Preferred)
java
class MyTask implements Runnable {
@Override
public void run() {
System.out.println("Task running");
}
}
Thread t = new Thread(new MyTask());
t.start();Method 3: Callable with Future
java
class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
return 42; // Can return value!
}
}
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(new MyCallable());
Integer result = future.get(); // Blocks until complete4. Interactive & Applied Code
java
import java.util.concurrent.*;
public class ThreadCreationDemo {
public static void main(String[] args) throws Exception {
// METHOD 1: Extend Thread
Thread t1 = new MyThread("Thread-1");
t1.start();
// METHOD 2: Runnable (anonymous class)
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Runnable via anonymous class");
}
});
t2.start();
// METHOD 3: Runnable (lambda - Java 8+)
Thread t3 = new Thread(() -> {
System.out.println("Runnable via lambda");
});
t3.start();
// METHOD 4: Callable with ExecutorService
ExecutorService executor = Executors.newSingleThreadExecutor();
Callable<String> task = () -> {
Thread.sleep(1000);
return "Result from Callable";
};
Future<String> future = executor.submit(task);
System.out.println("Waiting for result...");
String result = future.get(); // Blocking call
System.out.println("Got: " + result);
executor.shutdown();
// Demonstrate start() vs run()
demonstrateStartVsRun();
}
static void demonstrateStartVsRun() {
System.out.println("\n=== start() vs run() ===");
Thread t = new Thread(() -> {
System.out.println("Inside run(): " + Thread.currentThread().getName());
});
System.out.println("Calling run() directly:");
t.run(); // Runs in main thread!
System.out.println("Calling start():");
t.start(); // Creates new thread!
}
}
class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
@Override
public void run() {
System.out.println("MyThread running: " + getName());
}
}5. The Comparison & Decision Layer
| Method | Pros | Cons | Use When |
|---|---|---|---|
| Extend Thread | Simple | No multiple inheritance | Quick prototypes |
| Implement Runnable | Flexible, reusable | Verbose (pre-Java 8) | Production code |
| Callable | Returns value, throws exceptions | Requires Executor | Need results |
6. The "Interview Corner"
The "Killer" Interview Question: "Why is implementing Runnable preferred over extending Thread?" Answer:
- Java single inheritance: Can't extend another class if extending Thread
- Separation of concerns: Task (Runnable) separate from execution mechanism (Thread)
- Reusability: Same Runnable can be submitted to thread pools
java
// ❌ BAD: Locked into Thread
class MyTask extends Thread {
// Cannot extend DatabaseConnection!
}
// ✅ GOOD: Can extend whatever you need
class MyTask extends DatabaseConnection implements Runnable {
// Flexible!
}Pro-Tip: Always call start(), never run():
java
Thread t = new Thread(() -> System.out.println(Thread.currentThread().getName()));
t.run(); // ❌ Prints "main" (runs in current thread!)
t.start(); // ✅ Prints "Thread-0" (new thread created)