Skip to main content

Java Thread

1. Introduction

The Thread class in Java represents the smallest unit of execution in a program. It allows concurrent execution of multiple tasks, sharing the same process memory space. Understanding Thread is essential for mastering Java concurrency, as it forms the foundation for higher-level frameworks like ExecutorService, ForkJoinPool, and Virtual Threads.


2. Thread Fundamentals

Every thread in Java is an independent path of execution within a process. A program begins with the main thread, which can create additional threads to perform tasks concurrently.

Key Properties

  • Each thread runs a single task defined by a Runnable or by overriding run().
  • Threads share memory but maintain independent call stacks.
  • Thread scheduling and execution order are managed by the JVM and OS scheduler, not the developer.
Example
class Worker extends Thread {
public void run() {
System.out.println("Task running in: " + Thread.currentThread().getName());
}

public static void main(String[] args) {
Worker worker = new Worker();
worker.start(); // Start the thread
System.out.println("Main thread: " + Thread.currentThread().getName());
}
}

💡 Note: Calling start() creates a new thread, while calling run() directly executes the method in the current thread — no new thread is created.


3. Thread Lifecycle

A thread’s lifecycle is managed internally by the JVM. The states are defined in the Thread.State enum.

StateDescription
NEWThread object is created but not yet started.
RUNNABLEThread is ready or running — actual execution determined by scheduler.
BLOCKEDThread is waiting to acquire a monitor lock.
WAITINGThread is waiting indefinitely for another thread’s action (e.g., wait()).
TIMED_WAITINGThread is waiting for a specific time (e.g., sleep(1000)).
TERMINATEDThread has completed execution or exited due to an exception.
Example: Visualizing Lifecycle
Thread t = new Thread(() -> System.out.println("Running"));
System.out.println(t.getState()); // NEW
t.start();
System.out.println(t.getState()); // RUNNABLE or TERMINATED (depends on timing)

⚙️ Compiler Behavior: The JVM tracks thread state transitions internally; developers cannot manually change them.


4. Thread Priority

Thread priorities influence the scheduler’s choice of which thread to run first. However, they are merely hints — the JVM may ignore them depending on the underlying OS.

ConstantValueMeaning
MIN_PRIORITY1Lowest priority
NORM_PRIORITY5Default priority
MAX_PRIORITY10Highest priority
Example
Thread t1 = new Thread(() -> System.out.println("Low priority"));
Thread t2 = new Thread(() -> System.out.println("High priority"));

t1.setPriority(Thread.MIN_PRIORITY);
t2.setPriority(Thread.MAX_PRIORITY);

t1.start();
t2.start();

⚠️ Edge Case: High priority does not guarantee earlier execution — OS-level scheduling can override it.


5. Thread Constructors

Threads can be constructed in several ways depending on the execution target and naming.

ConstructorDescription
Thread()Creates a new thread with default name and no target.
Thread(Runnable target)Executes the target’s run() method when started.
Thread(Runnable target, String name)Same as above but assigns a custom name.
Thread(String name)Creates a named thread with no target.
Example
Runnable task = () -> System.out.println(Thread.currentThread().getName() + " executing task.");
Thread t = new Thread(task, "Worker-1");
t.start();

💡 Best Practice: Always assign meaningful thread names to simplify debugging and log tracing.


6. Important Static Methods

The Thread class provides several static utility methods to manage thread behavior globally.

MethodDescription
currentThread()Returns a reference to the currently executing thread.
sleep(long millis)Temporarily pauses execution for the specified time. (doesn’t release locks)
yield()Hints the scheduler to give other threads a chance to execute.
onSpinWait()Optimized CPU spin-wait hint introduced in Java 9.
Example
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName() + " - tick " + i);
Thread.sleep(500); // Pause for 0.5 seconds
}

7. The Interrupt Mechanism

Thread interruption is a cooperative mechanism to stop or signal a running thread. It doesn’t forcibly terminate a thread — instead, it sets an internal flag that the thread can check.

Example
class InterruptDemo {
public static void main(String[] args) throws InterruptedException {
Thread worker = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("Working...");
}
System.out.println("Stopped safely.");
});

worker.start();
Thread.sleep(1000);
worker.interrupt(); // Request stop
}
}

💡 Key Concept: The interrupt() method sets the interrupt flag. Methods like sleep() or wait() respond by throwing InterruptedException and clearing the flag.

Distinguishing interrupt() vs interrupted()

MethodTypeEffect
interrupt()Instance methodSets the interrupt flag of the target thread.
interrupted()Static methodChecks and clears the interrupt flag of the current thread.

⚠️ Edge Case: Calling interrupt() on another thread doesn’t guarantee immediate termination — the target thread must cooperatively check its status.


8. Edge Cases and Internal Behavior

  • Calling start() twice: Throws IllegalThreadStateException — a thread can only start once.
  • Thread safety: Multiple threads writing shared data without synchronization can lead to race conditions.
  • Memory visibility: Without synchronization, changes made by one thread may not be visible to another.
  • Finalization: Once a thread terminates, it cannot be restarted.

⚙️ Compiler Behavior Insight: The JVM uses a combination of native and managed calls for thread scheduling. Thread objects remain in memory until garbage collected, even after termination.

⚡️ wait() vs sleep()

🔹Aspect🔹wait()🔹sleep()
PurposeUsed for thread coordination — allows one thread to pause until another thread notifies it.Used for timed pauses or delays, usually to throttle execution or simulate waiting.
Lock releaseYes, releases the monitor lock of the object it’s called on.No, thread holds the lock (if any) while sleeping.
Requires synchronized blockYes, must be called inside a synchronized block or method, else IllegalMonitorStateException.No, can be called anywhere.
Belongs toObject class (because it’s tied to monitor/lock mechanics).Thread class (because it’s purely about pausing execution).
How thread wakes upBy another thread calling notify(), notifyAll(), or if the thread is interrupted.Automatically after the sleep duration ends or if interrupted.
Throws exceptionInterruptedExceptionInterruptedException
Common use caseProducer–Consumer pattern, thread coordination, or waiting for a condition to change.Throttling loops, retry delays, animation timing, etc.
Changes thread stateFrom RUNNABLE → WAITING / TIMED_WAITING depending on wait() overload.From RUNNABLE → TIMED_WAITING (for specified sleep duration).
Monitor behaviorReleases the monitor and lets other threads acquire it.Keeps the monitor locked — other threads are blocked if they need the same lock.
Control locationWorks on an object monitor, so it depends on synchronized object’s lock.Works on the current thread only.
Resumes executionAfter being notified or interrupted (and reacquiring the monitor).After time elapses or interruption.

⚡️ notify() vs notifyAll()

🔹Aspect🔹notify()🔹notifyAll()
PurposeWakes up one single thread that is waiting on the object's monitor.Wakes up all threads waiting on the object's monitor.
Belongs toObject classObject class
Requires synchronization✅ Yes — must be called within a synchronized block or method that locks the same object.✅ Yes — same as notify().
Thread state affectedOne thread moves from WAITING → BLOCKED (to re-acquire the lock)RUNNABLE.All waiting threads move from WAITING → BLOCKED (to re-acquire the lock), but only one thread actually proceeds first (others compete).
Lock behaviorDoes not release the lock immediately; lock is released after synchronized block ends.Same — lock is released only when synchronized block exits.
Use caseWhen you know exactly one thread needs to be resumed (e.g., single consumer for a single produced item).When multiple waiting threads might need to re-check a condition (e.g., multiple consumers waiting for items).
PerformanceSlightly more efficient — fewer threads are woken.Can be less efficient — may cause “thundering herd problem” (many threads waking unnecessarily).
Risk / Edge Case❗ If multiple threads are waiting but you call only notify(), others may starve indefinitely.✅ Safer when multiple waiting threads depend on shared conditions.
Thread schedulingThe thread to wake up is chosen arbitrarily by JVM — no guarantee which one.All waiting threads are awakened, but scheduling order is still unpredictable.
State changeChanges only one thread from WAITING → BLOCKED (for monitor) → RUNNABLE.Changes all waiting threads from WAITING → BLOCKED (for monitor) → RUNNABLE.