Extending Thread Class
Extending the Thread Class
The simplest way to create a thread is by extending java.lang.Thread.
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
try {
Thread.sleep(1000); // Sleep 1 second
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.start(); // Creates new thread, calls run()
t2.start(); // Creates new thread, calls run()
// Output (interleaved):
// Thread-0: 0
// Thread-1: 0
// Thread-0: 1
// Thread-1: 1
// ...
}
}
Key points:
- Override
run()with your task code - Call
start()to create a new thread - Never call
run()directly (runs on same thread) - Each thread gets a default name (Thread-0, Thread-1, etc.)
Implementing Runnable Interface
Implementing Runnable Interface
Runnable is a functional interface with a single run() method. This is the preferred approach because:
- Java doesn't support multiple inheritance
- Runnable separates task from thread
- Can be used with ExecutorService
// Lambda approach (Java 8+)
public class RunnableDemo {
public static void main(String[] args) {
Runnable task = () -> {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread t1 = new Thread(task, "Worker-1");
Thread t2 = new Thread(task, "Worker-2");
t1.start();
t2.start();
}
}
Class-based approach:
public class MyTask implements Runnable {
@Override
public void run() {
System.out.println("Task running on: " + Thread.currentThread().getName());
}
public static void main(String[] args) {
Thread t = new Thread(new MyTask());
t.start();
}
}
When to use which:
Runnable: Preferred, more flexible, supports lambdaThread: Only when you need to override Thread methods (getName, setPriority)
Thread Lifecycle
Thread Lifecycle States
A thread can be in one of six states:
start()
NEW ──────────────> RUNNABLE ──────────────> TERMINATED
│ ▲ │
lock/ │ │ notify/ │
wait │ │ timeout │
▼ │ │
BLOCKED/WAITING/TIMED_WAITING
1. NEW - Thread created but not started
Thread t = new Thread(() -> {}); // NEW state
2. RUNNABLE - Ready to run, waiting for CPU
t.start(); // Moves to RUNNABLE
3. BLOCKED - Waiting for monitor lock
synchronized (lock) {
// If another thread holds lock, this thread is BLOCKED
}
4. WAITING - Waiting indefinitely for another thread
thread.wait(); // WAITING
thread.join(); // WAITING (if thread is still running)
LockSupport.park(); // WAITING
5. TIMED_WAITING - Waiting for specified time
Thread.sleep(1000); // TIMED_WAITING
thread.wait(1000); // TIMED_WAITING
thread.join(1000); // TIMED_WAITING
6. TERMINATED - Execution complete
// After run() finishes
Check state:
Thread.State state = thread.getState();
System.out.println(state); // RUNNABLE, BLOCKED, etc.
Common Thread Methods
Common Thread Methods
sleep(long millis) - Pause current thread
Thread.sleep(1000); // Sleep 1 second
Thread.sleep(1000, 500000); // 1 second + 500 nanoseconds
// Throws InterruptedException
join() - Wait for thread to complete
Thread t1 = new Thread(() -> {
try { Thread.sleep(2000); } catch (Exception e) {}
System.out.println("Done");
});
t1.start();
t1.join(); // Main thread waits for t1 to finish
System.out.println("t1 completed");
// join with timeout
t1.join(5000); // Wait max 5 seconds
yield() - Hint to scheduler to give other threads a chance
Thread.yield(); // Hint only, no guarantee
interrupt() - Interrupt a sleeping/waiting thread
Thread t = new Thread(() -> {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
System.out.println("Thread interrupted!");
}
});
t.start();
Thread.sleep(100);
t.interrupt(); // Wakes up thread with InterruptedException
setPriority() - Set thread priority (1-10)
t.setPriority(Thread.MAX_PRIORITY); // 10
t.setPriority(Thread.MIN_PRIORITY); // 1
t.setPriority(Thread.NORM_PRIORITY); // 5 (default)
Thread.currentThread() - Get current thread reference
Thread current = Thread.currentThread();
System.out.println(current.getName());
Best Practices
Thread Best Practices
1. Prefer Runnable/Callable over extending Thread
// Bad - can't extend other classes
class MyThread extends Thread { ... }
// Good - flexible, supports lambda
Runnable task = () -> doWork();
new Thread(task).start();
// Better - use ExecutorService
ExecutorService executor = Executors.newFixedThreadPool(4);
executor.submit(task);
2. Always handle InterruptedException
// Bad - swallows exception
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// nothing
}
// Good - restores interrupt status
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // Restore status
// or propagate
}
3. Use meaningful thread names
// Bad
new Thread(() -> {}).start(); // Thread-0
// Good
new Thread(() -> {}, "OrderProcessor-1").start();
4. Don't rely on yield()
// yield() is just a hint
// Scheduler may ignore it
// Use proper synchronization instead
5. Use daemon threads for background tasks
Thread daemon = new Thread(() -> {
while (true) {
try { Thread.sleep(1000); } catch (Exception e) {}
System.out.println("Background task");
}
});
daemon.setDaemon(true); // Dies when main thread exits
daemon.start();
6. Never start a thread twice
Thread t = new Thread(() -> {});
t.start();
t.start(); // IllegalThreadStateException!
Practice Problems
What is the output of this code? Can you guarantee the order? ```java public class ThreadOrder { public static void main(String[] args) { Thread t1 = new Thread(() -> System.out.println("A")); Thread t2 = new Thread(() -> System.out.println("B")); Thread t3 = new Thread(() -> System.out.println("C")); t1.start(); t2.start(); t3.start(); } } ```
Solution
Output is **non-deterministic**. It could be ABC, ACB, BAC, BCA, CAB, or CBA.
To guarantee order:
```java
t1.start();
t1.join(); // Wait for t1
Thread t2 = new Thread(() -> System.out.println("B"));
t2.start();
t2.join(); // Wait for t2
Thread t3 = new Thread(() -> System.out.println("C"));
t3.start();
```
This ensures A → B → C ordering.What is the difference between these two code snippets? ```java // Version 1 MyThread t = new MyThread(); t.start(); // Version 2 MyThread t = new MyThread(); t.run(); ```
Solution
**Version 1 (`start()`):**
- Creates a new thread
- `run()` executes on the new thread
- Output: `Thread-0: ...` (new thread name)
- True parallelism
**Version 2 (`run()`):**
- No new thread created
- `run()` executes on the calling thread (main)
- Output: `main: ...` (main thread name)
- Sequential execution
**Key rule:** Always call `start()` to create a new thread. Calling `run()` directly is a common mistake.Quiz
1. What happens when you call run() instead of start()?
2. What is the difference between Runnable and Thread?
3. What method makes the current thread wait for another thread to finish?
4. What are the six thread states?
Flashcards
Question
What is the difference between start() and run()?
Click to reveal answer
Answer
start() creates a new thread and calls run() on it. run() executes on the current thread without creating a new one.
Question
What does Thread.join() do?
Click to reveal answer
Answer
Makes the current thread wait until the specified thread completes execution.
Question
What is the difference between sleep() and wait()?
Click to reveal answer
Answer
sleep() is a static method that pauses the current thread. wait() releases the object lock and waits for notify().
Question
When should you use a daemon thread?
Click to reveal answer
Answer
For background tasks that should stop when all non-daemon threads finish. Use setDaemon(true) before start().
Question
What is Java Threads and Concurrency?
Click to reveal answer
Answer
Java Threads and Concurrency is a key concept in Java programming.
Revision Notes
Key Takeaways
- 1.Always call start(), never call run() directly
- 2.Runnable is preferred over extending Thread
- 3.Thread lifecycle has 6 states
- 4.join() waits for a thread to complete
Interview Tips
- •Explain the difference between start() and run()
- •Know all 6 thread states
- •Explain why Runnable is preferred (multiple inheritance)
- •Discuss daemon threads and their use cases
Cheat Sheet
Threads Cheat Sheet
Creating Threads
- Extending Thread class
- Implementing Runnable (preferred)
- Using Callable (for return values)
Thread Methods
- start() → new thread
- run() → current thread
- sleep(ms) → pause
- join() → wait for completion
- yield() → hint to scheduler
- interrupt() → wake up sleeping thread
States
NEW → RUNNABLE → BLOCKED/WAITING/TIMED_WAITING → TERMINATED
Best Practices
- Prefer Runnable/Callable
- Handle InterruptedException
- Use meaningful thread names
- Never start thread twice