Java Multithreading Explained: Concepts, Lifecycle, and Practical Code Examples
Java applications often need to perform multiple tasks simultaneously. This tutorial walks you through single‑threaded versus multi‑threaded execution, the lifecycle of a thread, synchronization techniques, and hands‑on code examples that illustrate each concept.
What Is a Single Thread?
A single thread in Java is the smallest unit of execution. The Java runtime creates a user thread when the application starts, and you can add additional user or daemon threads as needed. Daemon threads run in the background and do not prevent the JVM from exiting.
Example of a single thread:
package demotest;
public class GuruThread {
public static void main(String[] args) {
System.out.println("Single Thread");
}
}
Advantages of single‑threaded execution:
- Minimal overhead—only one execution context is maintained.
- Simplified debugging and maintenance.
What Is Multithreading in Java?
Multithreading allows two or more threads to run concurrently, maximizing CPU utilization. Threads share the same memory space, so context switches are fast and memory usage stays low.
Example of creating two concurrent threads:
package demotest;
public class GuruThread1 implements Runnable {
public static void main(String[] args) {
Thread guruThread1 = new Thread("Guru1");
Thread guruThread2 = new Thread("Guru2");
guruThread1.start();
guruThread2.start();
System.out.println("Thread names are following:");
System.out.println(guruThread1.getName());
System.out.println(guruThread2.getName());
}
@Override
public void run() {
// Thread logic goes here
}
}
Benefits of multithreading:
- Non‑blocking UI and background processing.
- Resilience—an exception in one thread doesn’t halt others.
Thread Life Cycle in Java
The thread life cycle consists of five distinct states:
- New – Thread is instantiated but not started.
- Runnable – Thread has been started and is ready for execution.
- Running – Thread is actively executing.
- Waiting – Thread is suspended until a specific event occurs.
- Dead – Thread has finished execution.
Key Thread Methods
| Method | Description |
|---|---|
| start() | Begins thread execution; JVM calls run(). |
| sleep(long milliseconds) | Pauses the current thread for the specified duration. |
| getName() | Returns the thread’s name. |
| setPriority(int newPriority) | Adjusts thread priority (1–10). |
| yield() | Suggests the scheduler to pause the current thread. |
Practical Thread Example
Below is a concise example demonstrating thread creation, priority adjustment, and sleeping.
package demotest;
public class ThreadExample1 implements Runnable {
@Override
public void run() {
// Thread work here
}
public static void main(String[] args) {
Thread guruThread1 = new Thread();
guruThread1.start();
try {
Thread.sleep(1000); // pause for 1 second
} catch (InterruptedException e) {
e.printStackTrace();
}
guruThread1.setPriority(1);
System.out.println(guruThread1.getPriority());
System.out.println("Thread Running");
}
}
Output example:
Thread Synchronization
When multiple threads access shared resources, synchronization ensures data consistency. In Java, you can wrap critical sections with the synchronized keyword, guaranteeing exclusive access.
synchronized(object) {
// synchronized block
}
Advanced Multithreading Example
Below is a more elaborate example that creates two threads, each printing its name and a counter with a one‑second delay.
package demotest;
public class GuruThread2 {
public static void main(String[] args) {
GuruThread3 threadGuru1 = new GuruThread3("guru1");
threadGuru1.start();
GuruThread3 threadGuru2 = new GuruThread3("guru2");
threadGuru2.start();
}
}
class GuruThread3 implements Runnable {
private Thread guruthread;
private String guruname;
GuruThread3(String name) {
this.guruname = name;
}
@Override
public void run() {
System.out.println("Thread running " + guruname);
for (int i = 0; i < 4; i++) {
System.out.println(i);
System.out.println(guruname);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("Thread has been interrupted");
}
}
}
public void start() {
System.out.println("Thread started");
if (guruthread == null) {
guruthread = new Thread(this, guruname);
guruthread.start();
}
}
}
Typical output (illustrated in the accompanying image):
Takeaway
- Multithreading frees the application from blocking calls and enables concurrent operations.
- The thread life cycle—New, Runnable, Running, Waiting, Dead—defines the states a thread passes through.
- Synchronization is essential when threads share data, preventing race conditions.
- Practical coding examples reinforce understanding of thread creation, lifecycle management, and synchronization.
Java
- C++ Functions Explained with Practical Code Examples
- Java OOP Concepts: Fundamentals, Examples, and Advantages
- Mastering Java String.indexOf(): Locating Substrings & Practical Examples
- Mastering Java's String compareTo() Method: Syntax, Use Cases, and Practical Examples
- Constructor Overloading in Java – Explained with Practical Code Examples
- Master Java Reflection API: A Practical Guide with Code Examples
- Insertion Sort Algorithm in Java: A Clear Example & Step‑by‑Step Guide
- Selection Sort in Java: Step‑by‑Step Example & Full Code
- Mastering Java Multithreading: Efficient Concurrent Programming
- Mastering C++ Multithreading: Efficient Concurrent Programming