Navigation
Java Full Course: Mastering the Language Thread Creation in Java
Thread Creation in Java
1. Introduction to Threads
A thread is a lightweight unit of execution within a process. Java supports multithreading, allowing multiple threads to run concurrently, sharing the same memory space. Creating threads is the first step toward building responsive, parallel, or background‑task applications.
Java provides two primary ways to create a thread:
- Extend the
Threadclass. - Implement the
Runnableinterface.
Both approaches require you to define the code that the thread will execute in its run() method. The run() method contains the task logic.
2. Method 1: Extending the Thread Class
You create a new class that extends java.lang.Thread and override its run() method. To start the thread, create an instance of your class and call start() (never call run() directly).
Example:
Key points:
- The class can no longer extend any other class (Java single inheritance).
- The
run()method is overridden. - Starting the thread is done via
start(). - Each thread gets its own call stack and runs independently.
3. Method 2: Implementing the Runnable Interface
Implement java.lang.Runnable and define the run() method. Then pass an instance of this class to a Thread constructor.
Example:
Key points:
- The class can extend another class (since
Runnableis an interface). - The
Runnableobject is passed to theThreadconstructor. - Multiple threads can share the same
Runnableobject (useful for sharing data). - This approach is generally preferred because it separates the task from the threading mechanism.
4. Comparison: Thread vs Runnable
| Aspect | Extending Thread | Implementing Runnable |
|---|---|---|
| Inheritance | Cannot extend any other class | Can extend another class |
| Code reuse | Task code is tied to the thread class | Task can be reused with different threads or executors |
| Resource sharing | Separate instances for each thread | Multiple threads can share the same Runnable instance |
| Separation of concerns | Combines task logic with thread management | Separates task from thread management |
| Recommended usage | Rarely used | Preferred approach |
Note: Since Java 8, Runnable is a functional interface (single abstract method), so you can use lambda expressions to create threads even more concisely.
5. Creating Threads with Lambda Expressions (Java 8+)
Because Runnable has a single abstract method run(), it can be implemented using a lambda expression.
You can even inline the lambda directly in the Thread constructor:
This syntax is clean and widely used in modern Java applications.
6. Creating Threads with ExecutorService (Higher‑Level)
For more advanced thread management (e.g., thread pooling, scheduling), the java.util.concurrent package provides the ExecutorService framework. It abstracts thread creation and lifecycle.
Example using a fixed thread pool:
This approach is preferred in production code because it separates task submission from thread management and reuses threads efficiently.
7. Common Pitfalls and Best Practices
| Pitfall | Explanation / Solution |
|---|---|
Calling run() instead of start() | run() executes in the current thread, not a new one. Always call start(). |
| Starting a thread twice | A thread cannot be restarted; attempting to start() a terminated thread throws IllegalThreadStateException. |
Not handling InterruptedException | Methods like sleep() throw InterruptedException. Always handle it properly (e.g., restore interrupt status). |
Using Thread.stop() (deprecated) | It is unsafe and deprecated. Use flags or interruption to stop threads gracefully. |
| Sharing mutable data without synchronization | Leads to race conditions. Use synchronization, atomic classes, or concurrent collections. |
| Creating too many threads | Overhead degrades performance. Use thread pools. |
[!TIP] Best Practices:
- Prefer
Runnableover extendingThreadfor flexibility. - Use lambda expressions for concise task definitions.
- For long‑running or repeated tasks, use
ExecutorService. - Always give threads meaningful names (via constructor or
setName()) for easier debugging. - Handle interruptions gracefully; don’t swallow them.
- Avoid starting threads in constructors; it can expose partially constructed objects.
8. Complete Example: Thread Creation Showcase
Possible output (order may vary):
9. Key Points to Remember
- Two ways to create a thread: extend
Threador implementRunnable. - Start a thread with
start(), never callrun()directly. - Runnable is preferred because it separates task logic from thread management and allows extending another class.
- Lambda expressions make
Runnablecode concise. - ExecutorService provides a modern, scalable way to manage threads.
- Always name your threads for debugging.
- Handle interruptions properly.
- Avoid mutable shared data without synchronization.
