Java Notes
Complete guide to creating threads in Java — extending Thread class, implementing Runnable interface, lambda expressions, comparison of approaches, and when to use each method.
Two Ways to Create Threads
Java provides two fundamental approaches to create threads:
- Extending the
Threadclass - Implementing the
Runnableinterface
Both achieve the same goal but differ in flexibility and design philosophy.
Method 1: Extending the Thread Class
Output (interleaved):
| [Download] Step 1 (Thread | Downloader) |
| [Upload] Step 1 (Thread | Uploader) |
| [Download] Step 2 (Thread | Downloader) |
| [Upload] Step 2 (Thread | Uploader) |
| [Download] Step 3 (Thread | Downloader) |
| [Upload] Step 3 (Thread | Uploader) |
| [Download] Step 4 (Thread | Downloader) |
| [Upload] Step 4 (Thread | Uploader) |
| [Download] Step 5 (Thread | Downloader) |
| [Upload] Step 5 (Thread | Uploader) |
Method 2: Implementing the Runnable Interface
[Output of that]
Method 3: Anonymous Class
public class AnonymousThread {
public static void main(String[] args) throws InterruptedException {
System.out.println("=== Anonymous Class Threads ===\n");
// Anonymous Thread subclass
Thread t1 = new Thread() {
@Override
public void run() {
System.out.println("Anonymous Thread running: " + getName());
}
};
// Anonymous Runnable
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Anonymous Runnable running: " +
Thread.currentThread().getName());
}
}, "AnonRunnable");
t1.start();
t2.start();
t1.join();
t2.join();
}
}=== Anonymous Class Threads === Anonymous Thread running: Thread-0 Anonymous Thread running: Thread-1 Both anonymous threads completed.
Method 4: Lambda Expression (Java 8+) — Preferred
Output:
start() vs run() — Critical Difference!
public class StartVsRun {
public static void main(String[] args) {
Runnable task = () -> {
System.out.println(" Task running on: " + Thread.currentThread().getName());
};
Thread thread = new Thread(task, "MyThread");
System.out.println("=== start() vs run() ===\n");
System.out.println("Main thread: " + Thread.currentThread().getName());
// WRONG: Calling run() directly
System.out.println("\nCalling run() directly:");
thread.run(); // Runs on MAIN thread! No new thread created!
// CORRECT: Calling start()
System.out.println("\nCalling start():");
thread.start(); // Creates NEW thread, then calls run() on it
try { Thread.sleep(100); } catch (InterruptedException e) {}
}
}Output:
| Main thread | main |
| Task running on | main |
| Task running on | MyThread |
Key Rule: Always call start(), never run() directly!
start()→ new thread created, run() invoked on itrun()→ just a normal method call on the current thread
Thread vs Runnable — When to Use Which?
[Output of class]
Sharing a Runnable Among Multiple Threads
Output (may vary):
Real-World Example: Parallel File Downloader
Output:
| [DL-1] Starting | document.pdf (2.5 MB) |
| [DL-2] Starting | video.mp4 (150 MB) |
| [DL-3] Starting | archive.zip (45 MB) |
| [DL-4] Starting | image.png (3.2 MB) |
| [DL-5] Starting | database.sql (12 MB) |
| [DL-4] ✓ Completed | image.png (3.2 MB) [1/5] |
| [DL-1] ✓ Completed | document.pdf (2.5 MB) [2/5] |
| [DL-3] ✓ Completed | archive.zip (45 MB) [3/5] |
| [DL-5] ✓ Completed | database.sql (12 MB) [4/5] |
| [DL-2] ✓ Completed | video.mp4 (150 MB) [5/5] |
Common Mistakes
1. Calling run() instead of start()
// ❌ WRONG: No new thread created
thread.run(); // Executes on current thread
// ✅ CORRECT: New thread created
thread.start(); // New thread → calls run()2. Starting a thread twice
Thread t = new Thread(() -> System.out.println("Task"));
t.start();
t.start(); // ❌ IllegalThreadStateException!
// ✅ Create a new Thread instance for each execution3. Not handling InterruptedException
// ❌ BAD: Swallowing the interrupt
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// Empty — bad practice!
}
// ✅ GOOD: Restore interrupt status or handle properly
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // Restore interrupt flag
return; // Exit gracefully
}Interview Questions
Q1: What are the two ways to create a thread in Java?
Answer: (1) Extend Thread class and override run(), (2) Implement Runnable interface and pass to Thread constructor. Implementing Runnable is preferred because it allows extending another class and better separates task logic from threading mechanism.
Q2: What is the difference between start() and run()?
Answer: start() creates a new OS-level thread and invokes run() on that new thread. run() is just a normal method — calling it directly executes on the current thread with no new thread creation. Always use start() for multithreading.
Q3: Can we start a thread twice?
Answer: No. Calling start() on an already-started thread throws IllegalThreadStateException. Once a thread has terminated, it cannot be restarted. You must create a new Thread instance.
Q4: Why is implementing Runnable preferred over extending Thread?
Answer: (1) Java supports only single inheritance — extending Thread prevents extending other classes. (2) Runnable separates the task from the execution mechanism (better OOP design). (3) A single Runnable can be shared among multiple threads. (4) Runnable works with ExecutorService and thread pools.
Q5: Can a Runnable be used without creating a Thread object?
Answer: Yes! Runnables can be submitted to ExecutorService, used with ScheduledExecutorService, or executed directly with runnable.run() (though that won't create a new thread). The flexibility of Runnable is why it's preferred.
Q6: What is the difference between Callable and Runnable?
A: Runnable's run() returns void and cannot throw checked exceptions. Callable's call() returns a value (generic type V) and can throw checked exceptions. Callable is used with ExecutorService and returns a Future for retrieving the result asynchronously.
Q7: Can we create a thread using lambda expression?
A: Yes! Since Runnable is a functional interface (single abstract method), you can use lambdas: new Thread(() -> System.out.println("Running")).start(). This is the most concise way to create simple threads in Java 8+.
Q8: What happens if you call start() on a thread that's already started?
A: It throws IllegalThreadStateException. Once a thread has been started (regardless of whether it's still running or has terminated), it cannot be started again. You must create a new Thread instance for re-execution.
Q9: How do you pass data to a thread?
A: (1) Constructor parameters (in your Thread subclass or Runnable implementation), (2) Shared objects (passed by reference), (3) Instance variables, (4) Using Callable with Future for return values. Avoid sharing mutable state without synchronization.
Q10: What is the difference between user threads and daemon threads at creation time?
A: By default, new threads inherit the daemon status of their parent thread. Since main is a user thread, child threads are user threads by default. Call thread.setDaemon(true) before start() to make it a daemon thread. The JVM exits when only daemon threads remain.
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for Creating Threads in Java.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this Java Master Course topic.
Search Terms
java-master-course, java master course, java, master, course, multithreading, creating, thread
Related Java Master Course Topics