Mastering Java Thread Lifecycle: From NEW to TERMINATED

This article explains Java thread fundamentals, covering thread creation, lifecycle states, priority levels, daemon threads, initialization methods, and common thread operations such as join, yield, sleep, and interrupt, with detailed code examples and diagrams.

JavaEdge
JavaEdge
JavaEdge
Mastering Java Thread Lifecycle: From NEW to TERMINATED

1. Thread Basics

In the JVM, an application can run multiple concurrent threads. Each thread has a priority; higher‑priority threads are scheduled before lower‑priority ones. Threads may be daemon or non‑daemon.

When a new Thread object is created, its initial priority matches that of the creating thread, and it becomes a daemon only if the creator is a daemon.

The JVM starts with a non‑daemon thread that invokes the program's main method. The JVM continues running until either the Runtime.exit method is called (and permitted by the security manager) or all non‑daemon threads have terminated.

2. Thread Lifecycle

2.1 Thread States

The source code defines six thread states, illustrated by the following diagram:

Thread state diagram
Thread state diagram
NEW

– thread object created but not yet started. RUNNABLE – thread is executing or ready to run; after start() the thread enters this state. BLOCKED – waiting to acquire a monitor lock (e.g., entering a synchronized block). WAITING – waiting indefinitely for another thread (e.g., Object.wait, Thread.join, LockSupport.park). TIMED_WAITING – waiting with a timeout (e.g., Thread.sleep, timed wait). TERMINATED – thread has completed execution, been interrupted, or aborted.

2.2 Thread Priority

Priority determines the chance of a thread being scheduled. Java defines priorities from 1 (lowest) to 10 (highest); newly created threads default to 5. The following snippet shows the priority constants in the JDK source:

public static final int MIN_PRIORITY = 1;
public static final int NORM_PRIORITY = 5;
public static final int MAX_PRIORITY = 10;

2.3 Daemon Threads

Threads are non‑daemon by default. To create a daemon thread, set Thread.setDaemon(true) before starting it. Daemon threads have low priority and do not prevent the JVM from exiting; the JVM shuts down even if daemon threads are still running.

3. Creating Threads

There are two primary ways to create a thread without a return value:

3.1 Extending Thread

Override the run() method and start the thread with start(). The relevant source of Thread.start() is:

public synchronized void start() {
    if (threadStatus != 0) {
        throw new IllegalThreadStateException();
    }
    group.add(this);
    boolean started = false;
    try {
        start0();
        started = true;
    } finally {
        if (!started) {
            group.threadStartFailed(this);
        }
    }
}

private native void start0();

3.2 Implementing Runnable

Pass a Runnable instance to a Thread constructor. Calling start() launches a new thread that executes the run() method; invoking run() directly runs in the current thread.

4. Thread Initialization Details

The core initialization logic (simplified) is shown below:

public Thread() {
    init(null, null, "Thread-" + nextThreadNum(), 0);
}

private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc) {
    if (name == null) throw new NullPointerException("name cannot be null");
    this.name = name.toCharArray();
    Thread parent = currentThread();
    this.group = g;
    this.daemon = parent.isDaemon();
    this.priority = parent.getPriority();
    // class loader handling omitted for brevity
    this.target = target;
    setPriority(priority);
    if (parent.inheritableThreadLocals != null) {
        this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    }
    this.stackSize = stackSize;
    this.tid = nextThreadID();
}

Child threads inherit many attributes from their parent, including priority, daemon status, and inheritable thread‑local values.

5. Common Thread Operations

5.1 join()

join()

makes the current thread wait until another thread finishes. Example:

@Test
public void joinDemo() throws Exception {
    Thread main = Thread.currentThread();
    log.info("{} is run.", main.getName());
    Thread t = new Thread(() -> {
        log.info("{} begin run", Thread.currentThread().getName());
        try { Thread.sleep(30000L); } catch (InterruptedException e) { e.printStackTrace(); }
        log.info("{} end run", Thread.currentThread().getName());
    });
    t.start();
    t.join();
    log.info("{} is end", Thread.currentThread().getName());
}

During the join, the main thread is in TIMED_WAITING state.

5.2 yield()

yield()

is a native method that hints the scheduler to give other threads a chance to run:

yield source
yield source

It is useful in tight loops to avoid monopolizing the CPU.

5.3 sleep()

sleep()

is also native and can accept either milliseconds or milliseconds plus nanoseconds. While sleeping, the thread does not release locks, so other threads cannot acquire them.

5.4 interrupt()

Calling interrupt() sets the interrupt flag and causes methods such as wait(), join(), or sleep() to throw InterruptedException, moving the thread to TERMINATED after handling.

Example of interrupting a waiting thread:

@Test
public void interruptDemo() throws InterruptedException {
    Thread t = new Thread(() -> {
        log.info("{} begin run", Thread.currentThread().getName());
        try {
            log.info("Child thread sleeping 30s");
            Thread.sleep(30000L);
        } catch (InterruptedException e) {
            log.info("Child thread interrupted");
            e.printStackTrace();
        }
        log.info("{} end run", Thread.currentThread().getName());
    });
    t.start();
    Thread.sleep(1000L);
    log.info("Main thread waits 1s then interrupts child thread");
    t.interrupt();
}

If the child thread is in WAITING or TIMED_WAITING, the interrupt causes an InterruptedException and the thread eventually terminates.

Yield does not guarantee the thread will be skipped; the scheduler may still select it.
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaThread PriorityDaemon ThreadThread LifecycleThread Methods
JavaEdge
Written by

JavaEdge

First‑line development experience at multiple leading tech firms; now a software architect at a Shanghai state‑owned enterprise and founder of Programming Yanxuan. Nearly 300k followers online; expertise in distributed system design, AIGC application development, and quantitative finance investing.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.