9.1 Thread Fundamentals, Creation, and Lifecycle

Key Takeaways

  • Platform threads in Java map 1:1 to operating system kernel threads and can be created by subclassing Thread, implementing Runnable, or using the Java 21 Thread.ofPlatform() builder API.
  • The JVM thread lifecycle consists of six distinct states defined in Thread.State: NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, and TERMINATED.
  • Calling start() transitions a thread to RUNNABLE and spawns a new OS execution context; invoking run() directly merely executes the method synchronously on the caller thread without starting a new thread.
  • Thread interruption is a cooperative signaling mechanism: thread.isInterrupted() queries the status without clearing it, whereas static Thread.interrupted() tests and clears the current thread interrupt flag.
  • Daemon threads execute background tasks without preventing JVM termination when all non-daemon user threads finish; daemon status must be configured via setDaemon(true) before start() is called.
Last updated: September 2026

Thread Fundamentals, Creation, and Lifecycle

Concurrency is at the core of enterprise Java development and is heavily tested on the Oracle Certified Professional: Java SE 21 Developer (1Z0-830) exam. Understanding how the Java Virtual Machine (JVM) manages execution threads, transitions between thread states, coordinates cooperative task execution, and terminates processes is essential for passing the exam and writing robust software.


1. Concurrency, Parallelism, and Threads

Before diving into code, it is important to clarify key architectural definitions:

  • Process: An independent, isolated execution environment created by the operating system with its own private address space, memory pages, and system resources.
  • Thread: The smallest schedulable unit of execution within a process. All threads belonging to a process share the process's heap memory, static variables, and open file descriptors, but each thread maintains its own private Program Counter (PC), native stack, and call stack (local variables).
  • Concurrency vs. Parallelism:
    • Concurrency is the composition of independently executing processes or threads. It means managing multiple tasks by interleaving their execution over time (even on a single-core CPU via time-slicing).
    • Parallelism is the simultaneous execution of multiple computational tasks at the exact same physical instant across multiple distinct CPU cores or processors.

In traditional Java (prior to Java 21 virtual threads), every java.lang.Thread instance represents a Platform Thread (also called a carrier or kernel thread) that corresponds directly to a 1:1 mapping with an Operating System (OS) kernel thread.


2. Thread Creation Mechanisms

In Java SE 21, there are three primary approaches to define and start platform threads:

Approach A: Subclassing java.lang.Thread

You can extend java.lang.Thread and override its public void run() method:

public class WorkerThread extends Thread {
    public WorkerThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        System.out.println("Executing on thread: " + getName());
    }
}

// Usage
WorkerThread worker = new WorkerThread("Worker-1");
worker.start(); // Spawns a new OS thread and invokes run() asynchronously

[!WARNING] Subclassing Thread is generally discouraged in modern application design because Java only permits single class inheritance. Extending Thread prevents the class from extending any other domain superclass, tightens coupling between the unit of work and the execution mechanism, and wastes object overhead.


Approach B: Implementing java.lang.Runnable (Recommended Classic Approach)

Runnable is a @FunctionalInterface defining a single abstract method (SAM): public void run(). By passing a Runnable task to a Thread constructor, you cleanly separate the task to be performed from the threading execution mechanism:

// 1. Implementing Runnable via class
public class DatabaseTask implements Runnable {
    @Override
    public void run() {
        System.out.println("Processing database batch...");
    }
}

// 2. Lambda expression (anonymous Runnable)
Runnable task = () -> System.out.println("Running task on: " + Thread.currentThread().getName());

// 3. Method reference
Runnable cleanup = DatabaseTask::cleanupStaleConnections;

// Starting the thread
Thread t1 = new Thread(new DatabaseTask(), "DB-Thread");
Thread t2 = new Thread(task, "Lambda-Thread");
t1.start();
t2.start();

Approach C: The Java 21 Fluent Builder API (Thread.ofPlatform())

Java 21 introduces fluent builder APIs on the Thread class to construct platform and virtual threads with rich configurations (name prefixes, start indices, daemon flags, uncaught exception handlers):

// Create and immediately start a platform thread
Thread t1 = Thread.ofPlatform()
    .name("order-processor-", 1) // Names: order-processor-1, order-processor-2...
    .daemon(false)
    .priority(Thread.NORM_PRIORITY)
    .start(() -> System.out.println("Processing on " + Thread.currentThread().getName()));

// Create an unstarted thread instance
Thread t2 = Thread.ofPlatform()
    .name("batch-worker")
    .unstarted(() -> System.out.println("Batch execution started"));

t2.start(); // Explicit manual start

3. The Crucial Exam Trap: start() vs. run()

The difference between calling start() and run() is one of the most frequently tested topics on the 1Z0-830 exam:

Featurethread.start()thread.run()
Execution ContextRequests the JVM and OS to allocate a new call stack and schedule a new thread.Executes the run() method synchronously on the current calling thread.
New Thread Created?YesNo (runs on main or whichever thread invoked it)
Multiple InvocationsThrows java.lang.IllegalThreadStateException if called more than once.Can be invoked multiple times like any ordinary Java method.
Thread t = new Thread(() -> {
    System.out.println("Inside run(): " + Thread.currentThread().getName());
});

t.run();   // PRINTS: "Inside run(): main" (Synchronous method call!)
t.start(); // PRINTS: "Inside run(): Thread-0" (Asynchronous new thread!)
// t.start(); // RUNTIME ERROR: Throws IllegalThreadStateException!

4. The 6 JVM Thread Lifecycle States (Thread.State)

The JVM manages each thread according to the java.lang.Thread.State enumeration. You can query a thread's current state at any time by calling thread.getState().

StateDescriptionTypical Transitions In / Out
NEWThe thread instance has been constructed, but start() has not yet been invoked.Transitions to RUNNABLE when start() is called.
RUNNABLEThe thread is executing in the JVM or is ready and waiting for CPU allocation from the OS scheduler. This state encompasses both running and ready-to-run states, as well as waiting for OS resources like socket or file I/O.Transitions from NEW; transitions to BLOCKED, WAITING, TIMED_WAITING, or TERMINATED.
BLOCKEDThe thread is suspended, waiting to acquire an intrinsic monitor lock to enter or re-enter a synchronized method or statement block.Transitions to RUNNABLE once the monitor lock is acquired.
WAITINGThe thread is waiting indefinitely for another thread to perform a specific notification action. It does not consume CPU cycles.Enters via: Object.wait(), Thread.join(), or LockSupport.park(). Exits to BLOCKED or RUNNABLE upon notify(), notifyAll(), or target thread termination.
TIMED_WAITINGThe thread is waiting for another thread for up to a specified maximum duration.Enters via: Thread.sleep(millis), Object.wait(millis), Thread.join(millis), LockSupport.parkNanos(), or LockSupport.parkUntil(). Exits when timeout expires or upon notification.
TERMINATEDThe thread has completed execution because its run() method returned normally or threw an uncaught exception.Once TERMINATED, a thread can never be restarted.

5. Thread Coordination and Control Primitives

A. Thread.sleep(long millis) and TimeUnit

Thread.sleep() pauses the current thread for a specified duration without relinquishing any CPU locks or monitors it currently holds:

try {
    Thread.sleep(2000); // Sleep for 2000 milliseconds
    // Or more readably:
    TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
    // Thrown if another thread interrupts the sleeping thread
    Thread.currentThread().interrupt(); // Restore interrupt status flag
}

[!IMPORTANT] Thread.sleep() is a static method. Calling t.sleep(1000) on a thread reference t does not put thread t to sleep; it puts the currently executing thread to sleep!


B. Thread.join()

The join() method allows one thread to pause its execution and wait until another target thread has completed its execution (TERMINATED state):

Thread worker = new Thread(() -> {
    try { Thread.sleep(1000); } catch (InterruptedException e) {}
    System.out.println("Worker done.");
});

worker.start();
System.out.println("Main waiting for worker...");
worker.join(); // Main thread transitions to WAITING until worker terminates
// worker.join(500); // TIMED_WAITING: waits at most 500ms
System.out.println("Worker finished. Main resumes.");

C. Thread.yield()

Thread.yield() provides a non-binding heuristic hint to the operating system thread scheduler that the current thread is willing to yield its current use of a processor. The scheduler is free to ignore this hint. It does not throw InterruptedException and does not block the thread.


6. Cooperative Thread Interruption

Java does not support preemptive thread termination (methods like stop(), suspend(), and resume() are deprecated for removal and unsafe). Instead, Java employs a cooperative interruption mechanism using an internal boolean interrupt status flag.

The Interruption API

  1. thread.interrupt() (Instance method):
    • Sets the interrupt status flag of the target thread to true.
    • If the target thread is currently blocked in a call to Thread.sleep(), Object.wait(), or Thread.join(), the blocking call is aborted, an InterruptedException is thrown, and the interrupt status flag is automatically cleared (reset to false)!
  2. thread.isInterrupted() (Instance method):
    • Returns true if the target thread has been interrupted; does not clear or modify the flag.
  3. Thread.interrupted() (Static method):
    • Tests whether the current thread has been interrupted and clears the interrupt status flag (sets it to false).
// Standard cooperative cancellation loop pattern
Thread worker = new Thread(() -> {
    while (!Thread.currentThread().isInterrupted()) {
        try {
            // Perform work unit...
            Thread.sleep(100);
        } catch (InterruptedException e) {
            System.out.println("Interrupted during sleep! Flag was cleared.");
            // Crucial: Restore interrupt status or exit loop cleanly
            Thread.currentThread().interrupt(); // Restore flag
            break; // Terminate worker cleanly
        }
    }
    System.out.println("Worker cleaned up and exiting.");
});

worker.start();
worker.interrupt(); // Signal thread to stop

7. Daemon Threads

Threads in Java are categorized into two types:

  • User (Non-Daemon) Threads: High-priority application threads. The JVM will keep running as long as at least one non-daemon thread is alive.
  • Daemon Threads: Background service providers (e.g., garbage collection, finalizer, JVM telemetry). When all user threads terminate, the JVM immediately halts and terminates, abruptly killing all running daemon threads without executing remaining finally blocks or flushes.

Configuration Rules:

  1. A thread inherits the daemon status of the thread that created it.
  2. To configure a platform thread as a daemon, invoke thread.setDaemon(true) strictly before calling thread.start().
  3. Calling setDaemon(true) on a thread that is already running throws IllegalThreadStateException.
Thread daemonWorker = new Thread(() -> {
    while (true) {
        System.out.println("Background logging ping...");
        try { Thread.sleep(500); } catch (InterruptedException e) { break; }
    }
});

daemonWorker.setDaemon(true); // Must be called BEFORE start()
daemonWorker.start();
// When the main thread completes, the JVM exits immediately!

8. Thread Priority and Uncaught Exception Handlers

  • Thread Priorities: Integer values ranging from Thread.MIN_PRIORITY (1) to Thread.MAX_PRIORITY (10), defaulting to Thread.NORM_PRIORITY (5). Priorities serve only as hints to the OS scheduler and do not guarantee execution order.
  • Thread.UncaughtExceptionHandler: An interface to capture uncaught exceptions escaping from a thread's run() method:
Thread t = new Thread(() -> {
    throw new RuntimeException("Fatal failure in worker!");
});

t.setUncaughtExceptionHandler((thread, throwable) -> {
    System.err.println("Thread " + thread.getName() + " threw: " + throwable.getMessage());
});

t.start();
Loading diagram...
Java Thread Lifecycle and State Transitions
Test Your Knowledge

What is the output of compiling and running the following Java program?

public class ThreadTest {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            System.out.print("Running ");
        });
        t.run();
        t.start();
    }
}

A
B
C
D
Test Your Knowledge

A developer creates a platform thread and attempts to configure its daemon status after launching it:

Thread t = new Thread(() -> {
    try { Thread.sleep(2000); } catch (InterruptedException e) {}
});
t.start();
t.setDaemon(true);
What happens when this code is executed?

A
B
C
D
Test Your Knowledge

A worker thread is sleeping inside a try-catch block when another thread calls worker.interrupt(). What occurs inside the worker thread?

try {
    Thread.sleep(5000);
} catch (InterruptedException e) {
    System.out.println(Thread.currentThread().isInterrupted());
}

A
B
C
D
Test Your Knowledge

Which of the following describes the exact condition that causes a Java thread to enter the BLOCKED state defined in java.lang.Thread.State?

A
B
C
D