Skip to content

线程创建与生命周期

概述

线程(Thread) 是程序执行的最小单位。一个 Java 进程可以包含多个线程,它们共享进程的内存空间,从而能够并发执行多段代码。掌握线程的创建与生命周期,是学习 同步与锁线程池 的前提。本文介绍两种创建线程的方式、线程的六种状态以及 startrunsleepjoin 等常用方法。


线程的两种创建方式

Java 中创建并运行一条新线程,通常有两种方式:继承 Thread实现 Runnable 接口。推荐使用实现 Runnable,便于与类继承体系解耦、便于与线程池等 API 配合。

方式一:继承 Thread 类

子类继承 java.lang.Thread,重写 run(),在 run() 中编写线程要执行的逻辑;然后创建该子类实例并调用 start() 启动线程。

java
public class MyThread extends Thread {
    @Override
    public void run() {
        // 线程要执行的代码
        for (int i = 0; i < 3; i++) {
            System.out.println(Thread.currentThread().getName() + ": " + i);
        }
    }

    public static void main(String[] args) {
        Thread t = new MyThread();
        t.start();  // 启动新线程,由新线程执行 run()
        // 主线程继续往下执行
        System.out.println("main 线程");
    }
}

注意

必须调用 start() 才会在新线程中执行 run()。若直接调用 run(),只是在当前线程中普通方法调用,不会创建新线程。

方式二:实现 Runnable 接口(推荐)

实现 Runnable 接口的 run(),将实现类实例传给 Thread 的构造方法,再调用 Thread#start() 启动。

java
public class RunnableDemo {
    public static void main(String[] args) {
        Runnable task = () -> {
            for (int i = 0; i < 3; i++) {
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        };
        Thread t = new Thread(task, "工作线程");
        t.start();
    }
}

提示

实现 Runnable 后,同一个 Runnable 实例可以传给多个 Thread,多线程共享同一段逻辑(若逻辑内部有共享变量,则需考虑同步与锁)。且类可以继续继承其他类,不受 Java 单继承限制。


基本示例

示例 1:区分 start() 与 run()

java
public class StartVsRun {
    public static void main(String[] args) {
        Thread t = new Thread(() -> System.out.println("执行线程: " + Thread.currentThread().getName()));

        // 错误做法:直接调用 run(),只在 main 线程中执行
        t.run();   // 输出: 执行线程: main

        // 正确做法:start() 会启动新线程并执行 run()
        t.start(); // 输出: 执行线程: Thread-0(或类似名称)
    }
}

示例 2:为线程命名与 sleep

java
public class NamedThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + " 开始");
            try {
                Thread.sleep(1000);  // 当前线程休眠 1 秒,不释放锁
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            System.out.println(Thread.currentThread().getName() + " 结束");
        }, "我的工作线程");
        t.start();
        t.join();  // 主线程等待 t 结束
        System.out.println("main 结束");
    }
}

Thread.sleep(long millis) 会抛出受检异常 InterruptedException,需捕获或声明抛出;在 catch 中通常调用 Thread.currentThread().interrupt() 恢复中断状态,便于上层处理。

示例 3:join() 等待线程结束

java
public class JoinDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            try {
                Thread.sleep(500);
                System.out.println("子线程完成");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
        t.start();
        System.out.println("主线程等待子线程...");
        t.join();  // 主线程阻塞,直到 t 终止
        System.out.println("主线程继续");
    }
}

线程生命周期与状态

Java 中线程在任意时刻处于以下六种状态之一(Thread.State 枚举):

状态说明
NEW已创建未启动(new Thread() 后未调用 start()
RUNNABLE可运行(包括正在运行或就绪等待 CPU 调度)
BLOCKED等待获取监视器锁(如等待进入 synchronized 块)
WAITING无限期等待(如 Object.wait()Thread.join() 无参)
TIMED_WAITING限时等待(如 Thread.sleep()Thread.join(long)Object.wait(long)
TERMINATED线程已结束(run() 正常返回或抛出未捕获异常)

状态转换可简记为:NEW → RUNNABLE ↔ BLOCKED/WAITING/TIMED_WAITING → TERMINATED。调用 start() 后从 NEW 进入 RUNNABLE;在等待锁或调用 sleep/join/wait 时进入 BLOCKED/WAITING/TIMED_WAITING;run() 结束后进入 TERMINATED。

java
Thread t = new Thread(() -> {});
System.out.println(t.getState());  // NEW
t.start();
System.out.println(t.getState());  // RUNNABLE(或很快变为 TIMED_WAITING 等,视实现而定)

常用方法速览

方法说明
start()启动线程,由 JVM 在新线程中调用 run()
run()线程要执行的逻辑;不应直接调用,应通过 start() 间接执行
static sleep(long millis)当前线程休眠指定毫秒,进入 TIMED_WAITING
join() / join(long millis)当前线程等待本线程终止(无限或限时)
static currentThread()获取当前执行线程的引用
getName() / setName(String)线程名称(便于调试与日志)
getId()线程 ID(唯一,不可变)
getState()当前状态(Thread.State
isAlive()是否已启动且未终止

补充

若需要线程返回结果,可使用 Callable + Future,在线程池中提交任务并获取返回值;详见 线程池


注意事项

注意

  • 不要直接调用 run():只有 start() 才会真正启动新线程;直接 run() 只是同步方法调用。
  • 不要对同一 Thread 实例多次调用 start():一旦线程已启动或已终止,再次 start() 会抛出 IllegalThreadStateException

提示

  • 线程命名:构造时传入名称或 setName(),便于日志与排查问题。
  • 捕获 InterruptedException 时,通常应调用 Thread.currentThread().interrupt() 恢复中断状态,以便上层或后续代码能感知中断。

相关链接

基于 VitePress 构建