---
url: /doc/Java/thread-fundamentals/index.md
---
# Java 线程基础

本文全面覆盖 Java 线程核心基础知识，包括生命周期、创建方式、守护线程、调度优先级、线程组、ThreadLocal、中断机制以及线程协作方法。

***

## 1. 线程生命周期（Thread Lifecycle）

### 核心概念

Java 线程在生命周期中会经历 6 种状态，定义在 `Thread.State` 枚举中：

| 状态 | 说明 |
| :--- | :--- |
| **NEW** | 线程对象已创建，但尚未调用 `start()` |
| **RUNNABLE** | 调用 `start()` 后，线程处于可运行状态（包含操作系统的 Ready 和 Running） |
| **BLOCKED** | 线程等待获取 synchronized 锁（锁池） |
| **WAITING** | 线程无限期等待（等待池），等待其他线程显式唤醒 |
| **TIMED\_WAITING** | 线程在指定时间内等待，超时后自动返回 |
| **TERMINATED** | 线程执行完毕或因异常退出 |

### 状态转换图

```mermaid
stateDiagram-v2
    [*] --> NEW : new Thread()

    NEW --> RUNNABLE : start()

    state RUNNABLE {
        [*] --> Ready
        Ready --> Running : CPU调度
        Running --> Ready : yield() / 时间片用完
    }

    RUNNABLE --> BLOCKED : 等待synchronized锁
    BLOCKED --> RUNNABLE : 获取到锁

    RUNNABLE --> WAITING : wait() / join() / LockSupport.park()
    WAITING --> RUNNABLE : notify() / notifyAll() / join完成 / LockSupport.unpark()

    RUNNABLE --> TIMED_WAITING : sleep(ms) / wait(ms) / join(ms) / LockSupport.parkNanos()
    TIMED_WAITING --> RUNNABLE : 超时自动唤醒 / notify() / notifyAll()

    RUNNABLE --> TERMINATED : run()执行完毕 / 异常退出
    TERMINATED --> [*]
```

### 关键 API 签名

```java
// 获取线程当前状态
public State getState()

// 线程是否存活（RUNNABLE / BLOCKED / WAITING / TIMED_WAITING 均为 true）
public final boolean isAlive()
```

### 代码示例

```java
Thread t = new Thread(() -> System.out.println("running"));
System.out.println(t.getState()); // NEW

t.start();
System.out.println(t.getState()); // RUNNABLE

t.join();
System.out.println(t.getState()); // TERMINATED
```

### 常见陷阱

* **RUNNABLE 包含了操作系统的 Ready 和 Running 两种子状态**，Java 层面不区分
* 调用 `start()` 后线程并不会立即执行，而是进入 RUNNABLE 状态等待 CPU 调度
* 对已启动的线程重复调用 `start()` 会抛出 `IllegalThreadStateException`
* `BLOCKED` 仅针对 synchronized 锁等待，Lock 框架的等待对应 `WAITING` 或 `TIMED_WAITING`

### 最佳实践

* 通过 `getState()` 进行调试和监控，不要在生产逻辑中依赖线程状态做业务判断
* 始终检查 `isAlive()` 而不是 `getState() != TERMINATED` 来判断线程是否还在运行

***

## 2. 线程创建方式

### 方式一：继承 Thread 类

#### 核心概念

* Thread 类本身实现了 `Runnable` 接口
* 继承 Thread 后重写 `run()` 方法定义线程执行体
* 由于 Java 单继承限制，该方式灵活性较低
* 多个线程之间**无法共享**线程类的实例变量

#### 关键 API 签名

```java
public class Thread implements Runnable {
    public Thread();
    public Thread(String name);
    public Thread(Runnable target);
    public Thread(Runnable target, String name);

    public void start();                    // 启动线程，JVM 调用 run()
    public void run();                      // 线程执行体
}
```

#### 代码示例

```java
public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println(getName() + " is running");
    }
}

// 使用
Thread t = new MyThread();
t.setName("worker-1");
t.start();
```

### 方式二：实现 Runnable 接口

#### 核心概念

* `Runnable` 是函数式接口（`@FunctionalInterface`），只有一个 `run()` 方法
* 将任务（Runnable）与线程（Thread）解耦，更符合单一职责原则
* 多个线程可以**共享同一个** Runnable 对象的实例变量

#### 关键 API 签名

```java
@FunctionalInterface
public interface Runnable {
    void run();
}
```

#### 代码示例

```java
Runnable task = () -> System.out.println(Thread.currentThread().getName() + " is running");
new Thread(task, "worker-1").start();

// 多线程共享同一个 target
Runnable sharedTask = () -> { /* 访问共享资源 */ };
new Thread(sharedTask, "A").start();
new Thread(sharedTask, "B").start();
```

### 方式三：实现 Callable + FutureTask

#### 核心概念

* `Callable<V>` 是有返回值的任务接口，`call()` 方法可以抛出受检异常
* `FutureTask<V>` 同时实现了 `Runnable` 和 `Future<V>`，可以作为 Thread 的 target
* 适合需要获取线程执行结果的场景

#### 关键 API 签名

```java
@FunctionalInterface
public interface Callable<V> {
    V call() throws Exception;
}

public class FutureTask<V> implements RunnableFuture<V> {
    public FutureTask(Callable<V> callable);
    public FutureTask(Runnable runnable, V result);
}

public interface Future<V> {
    V get() throws InterruptedException, ExecutionException;
    V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
}
```

#### 代码示例

```java
Callable<String> task = () -> {
    Thread.sleep(1000);
    return "result from " + Thread.currentThread().getName();
};

FutureTask<String> futureTask = new FutureTask<>(task);
new Thread(futureTask, "callable-worker").start();

// get() 会阻塞直到线程返回结果
String result = futureTask.get(2, TimeUnit.SECONDS);
System.out.println(result);
```

### 三种方式对比

| 特性 | extends Thread | implements Runnable | implements Callable |
| :--- | :--- | :--- | :--- |
| 返回值 | 无 | 无 | 有 (`V call()`) |
| 异常 | 无受检异常 | 无受检异常 | 可抛出受检异常 |
| 继承限制 | 单继承 | 无 | 无 |
| 共享变量 | 不支持 | 支持（共享 target） | 支持（共享 target） |
| 获取当前线程 | `this` | `Thread.currentThread()` | `Thread.currentThread()` |

### 常见陷阱

* 直接调用 `run()` 而非 `start()`：这会在当前线程中同步执行，不会创建新线程
* `FutureTask.get()` 是阻塞调用，如果不设置超时可能导致主线程永远阻塞
* Callable 的 `call()` 抛出的异常会被包装为 `ExecutionException`

### 最佳实践

* 优先使用 `Runnable` 或 `Callable` 接口，避免继承 Thread
* 实际生产中应使用线程池（`ExecutorService`）管理线程，而非手动 `new Thread()`

***

## 3. 守护线程（Daemon Thread）

### 核心概念

* **守护线程**（Daemon Thread）为其他线程（用户线程）提供服务，如 GC 线程
* **用户线程**（User Thread）是应用程序的前台工作线程
* 当所有用户线程结束后，JVM 会退出，所有守护线程随之自动销毁
* 守护线程中创建的子线程默认也是守护线程

### 关键 API 签名

```java
public final void setDaemon(boolean on)    // 必须在 start() 前调用
public final boolean isDaemon()
```

### 代码示例

```java
Thread daemon = new Thread(() -> {
    while (true) {
        System.out.println("daemon working...");
        try { Thread.sleep(500); } catch (InterruptedException e) { break; }
    }
});
daemon.setDaemon(true);  // 必须在 start() 之前设置
daemon.start();

// 主线程（用户线程）结束后，daemon 线程会自动终止
Thread.sleep(2000);
System.out.println("main thread exits");
```

### 常见陷阱

* 在 `start()` 之后调用 `setDaemon(true)` 会抛出 `IllegalThreadStateException`
* 守护线程的 `finally` 块不一定会执行（JVM 退出时不等待守护线程完成），因此不要在守护线程中执行需要善后的 I/O 操作
* 守护线程中不要操作需要持久化的资源（文件、数据库等），因为随时可能被强制终止

### 最佳实践

* 守护线程适合用于后台监控、日志采集、心跳检测等辅助性任务
* 线程池中的线程默认是用户线程；通过自定义 `ThreadFactory` 可以创建守护线程

***

## 4. 线程调度与优先级

### 核心概念

* Java 采用**抢占式调度**（Preemptive Scheduling），线程的执行顺序由 JVM 和操作系统共同决定
* 线程优先级是一个**提示性**参数（1~10），不保证执行顺序
* 优先级高的线程仅表示获得 CPU 时间片的概率更大，不意味着一定会先执行

### 关键 API 签名

```java
// 优先级常量
public static final int MIN_PRIORITY = 1;
public static final int NORM_PRIORITY = 5;   // 默认优先级
public static final int MAX_PRIORITY = 10;

// 实例方法
public final int getPriority()
public final void setPriority(int newPriority)
```

### 代码示例

```java
Thread high = new Thread(() -> System.out.println("high priority"));
Thread low = new Thread(() -> System.out.println("low priority"));

high.setPriority(Thread.MAX_PRIORITY);
low.setPriority(Thread.MIN_PRIORITY);

low.start();
high.start();
// 输出顺序不确定，但 high 优先获得 CPU 的概率更大
```

### 常见陷阱

* 不同操作系统对线程优先级的映射不同，Windows 有 7 个级别，Linux 可能完全忽略优先级
* 不要依赖优先级来保证程序正确性，它只是一种性能优化手段
* 子线程默认继承父线程的优先级

### 最佳实践

* 绝大多数情况下使用默认优先级（`NORM_PRIORITY`）即可
* 如果确实需要调整，优先级差距要足够大（如 1 vs 10）才有明显效果

***

## 5. 线程组（ThreadGroup）

### 核心概念

* `ThreadGroup` 用于对一组线程进行集中管理（批量操作）
* 每个线程必定属于一个线程组，默认与创建它的父线程同组
* 主线程的线程组名为 `"main"`
* 在现代 Java 开发中，线程组的使用已逐渐减少，推荐使用线程池替代

### 关键 API 签名

```java
// 构造器
public ThreadGroup(String name)
public ThreadGroup(ThreadGroup parent, String name)

// 常用方法
public int activeCount()                          // 估算活跃线程数
public int enumerate(Thread[] list)               // 将线程复制到数组
public String getName()
public ThreadGroup getParent()
public void interrupt()                            // 中断组内所有线程
public boolean isDaemon()
public void setDaemon(boolean daemon)
public void uncaughtException(Thread t, Throwable e)  // 处理未捕获异常
```

### 代码示例

```java
ThreadGroup group = new ThreadGroup("my-group");

Thread t1 = new Thread(group, () -> System.out.println("task-1"), "worker-1");
Thread t2 = new Thread(group, () -> System.out.println("task-2"), "worker-2");

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

System.out.println("活跃线程数: " + group.activeCount());

// 批量中断组内所有线程
group.interrupt();
```

### 常见陷阱

* `activeCount()` 返回的是估算值，可能不准确
* 线程组内的 `destroy()` 方法只能销毁没有任何活跃线程的组
* 线程组本身已经是一种过时的 API，`java.util.concurrent` 包提供了更好的替代方案

### 最佳实践

* 新项目中优先使用 `ExecutorService` 线程池，而非 ThreadGroup
* 如果需要统一处理未捕获异常，使用 `Thread.setUncaughtExceptionHandler()` 更灵活

***

## 6. ThreadLocal 与 InheritableThreadLocal

### ThreadLocal 核心概念

* `ThreadLocal` 为每个线程维护一个独立的变量副本，实现**线程隔离**
* 内部通过 `ThreadLocalMap`（定义在 Thread 类中）存储数据，key 为 ThreadLocal 实例，value 为线程的变量副本
* 常用于存储线程上下文信息（用户身份、数据库连接、日期格式化器等）

### 关键 API 签名

```java
public class ThreadLocal<T> {
    public ThreadLocal();
    public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier);
    public T get();
    public void set(T value);
    public void remove();
    protected T initialValue();
}
```

### 代码示例

```java
// 方式一：覆写 initialValue
private static final ThreadLocal<SimpleDateFormat> sdfHolder =
    new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd");
        }
    };

// 方式二：使用 withInitial（推荐）
private static final ThreadLocal<SimpleDateFormat> sdfHolder =
    ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));

// 使用
SimpleDateFormat sdf = sdfHolder.get();  // 每个线程获取独立的实例
sdfHolder.remove();  // 用完后清理，防止内存泄漏
```

### InheritableThreadLocal 核心概念

* `InheritableThreadLocal` 是 `ThreadLocal` 的子类
* 子线程创建时会**自动继承**父线程中 `InheritableThreadLocal` 的值
* 仅在子线程创建时复制一次，之后父子线程的值相互独立

#### 代码示例

```java
private static final InheritableThreadLocal<String> context = new InheritableThreadLocal<>();

public static void main(String[] args) {
    context.set("parent-value");

    Thread child = new Thread(() -> {
        System.out.println("子线程获取: " + context.get()); // "parent-value"
        context.set("child-value");
        System.out.println("子线程修改后: " + context.get()); // "child-value"
    });
    child.start();
    child.join();

    System.out.println("父线程值不变: " + context.get()); // "parent-value"
}
```

### 常见陷阱

* **内存泄漏**：ThreadLocalMap 的 key 是弱引用（ThreadLocal 实例），但 value 是强引用。如果线程长期存活（如线程池），value 不会被 GC 回收。**必须在 finally 块中调用 `remove()`**
* **线程池场景**：线程复用时，上一次设置的 ThreadLocal 值会残留，导致数据串线。必须在任务执行前后进行 `set()` / `remove()`
* `InheritableThreadLocal` 在线程池场景下不可靠，因为线程池中的线程是复用的而非新建的，不会触发继承。推荐使用阿里开源的 `TransmittableThreadLocal`（TTL）解决

### 最佳实践

* ThreadLocal 变量声明为 `private static final`
* 始终在使用后调用 `remove()`，最好放在 `try-finally` 中
* 在线程池场景下，使用 `try-finally` 确保每次任务执行后清理 ThreadLocal

```java
// 线程池中使用 ThreadLocal 的标准模式
executor.submit(() -> {
    try {
        context.set(userInfo);
        doBusiness();
    } finally {
        context.remove();  // 必须！
    }
});
```

***

## 7. 线程中断机制

### 核心概念

* Java 没有安全的方式强制停止线程，因此采用**协作式中断**
* 每个线程都有一个 `interrupted` 标志位
* 调用 `interrupt()` 会设置该标志位，但不会强制终止线程
* 线程通过**主动检查**中断标志来决定是否停止

### 关键 API 签名

```java
// 实例方法 —— 设置中断标志
public void interrupt()

// 实例方法 —— 检查中断标志（不清除）
public boolean isInterrupted()

// 静态方法 —— 检查并清除当前线程的中断标志
public static boolean interrupted()
```

### 中断行为分类

| 线程当前状态 | 调用 interrupt() 的效果 |
| :--- | :--- |
| RUNNABLE（正常运行） | 仅设置中断标志位为 true，线程继续运行 |
| WAITING / TIMED\_WAITING（wait/sleep/join） | 清除中断标志，抛出 `InterruptedException` |
| BLOCKED（等待 synchronized 锁） | 仅设置中断标志位，线程继续等待锁 |
| Lock.lock() 等待中 | 仅设置中断标志位（需用 `lockInterruptibly()` 才能响应中断） |

### 代码示例

#### 方式一：主动检查中断标志

```java
Thread t = new Thread(() -> {
    while (!Thread.currentThread().isInterrupted()) {
        // 执行任务
        System.out.println("working...");
    }
    System.out.println("线程正常退出");
});
t.start();
Thread.sleep(100);
t.interrupt();  // 请求中断
```

#### 方式二：捕获 InterruptedException

```java
Thread t = new Thread(() -> {
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        // InterruptedException 会清除中断标志！
        System.out.println("被中断，中断标志: " + Thread.currentThread().isInterrupted()); // false
        // 如果需要保留中断状态，应重新设置：
        // Thread.currentThread().interrupt();
    }
});
t.start();
t.interrupt();
```

### 常见陷阱

* `InterruptedException` 被捕获后中断标志会被**自动清除**，不要吞掉异常而不做任何处理
* `Thread.interrupted()` 是静态方法，检查并清除**当前线程**的中断标志；`isInterrupted()` 是实例方法，不会清除标志
* 不要在生产代码中使用已废弃的 `stop()`、`suspend()`、`resume()` 方法

### 最佳实践

```java
// 正确处理 InterruptedException 的方式
// 方式一：向上抛出
public void run() throws InterruptedException {
    // ... 业务代码
}

// 方式二：捕获后恢复中断状态
catch (InterruptedException e) {
    Thread.currentThread().interrupt();  // 恢复中断标志
    // 或者 break/return 退出循环
}
```

***

## 8. Thread.join()、Thread.yield()、Thread.sleep()

### Thread.sleep()

#### 核心概念

* 让当前线程暂停执行指定的毫秒数，进入 `TIMED_WAITING` 状态
* **不释放任何锁**（synchronized 锁、Lock 锁都不会释放）
* 到期后线程回到 RUNNABLE 状态，等待 CPU 调度

#### 关键 API 签名

```java
public static native void sleep(long millis) throws InterruptedException;
public static void sleep(long millis, int nanos) throws InterruptedException;
```

#### 代码示例

```java
// 简易重试机制
for (int i = 0; i < 3; i++) {
    if (tryConnect()) break;
    Thread.sleep(1000 * (i + 1));  // 递增等待
}

// Java 9+ 支持 Duration
Thread.sleep(Duration.ofSeconds(2));
```

### Thread.join()

#### 核心概念

* 让当前线程等待目标线程执行完毕
* 底层通过 `wait()` 实现（在目标线程对象上等待）
* 当目标线程终止时，会自动调用 `notifyAll()` 唤醒所有等待的线程

#### 关键 API 签名

```java
public final void join() throws InterruptedException            // 无限等待
public final synchronized void join(long millis) throws InterruptedException  // 限时等待
public final synchronized void join(long millis, int nanos) throws InterruptedException
```

#### 代码示例

```java
Thread t1 = new Thread(() -> { /* 耗时任务 */ });
Thread t2 = new Thread(() -> { /* 耗时任务 */ });

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

// 等待两个线程都完成
t1.join();
t2.join();
System.out.println("所有任务完成");
```

### Thread.yield()

#### 核心概念

* 提示调度器当前线程愿意让出 CPU 时间片，进入 RUNNABLE 状态（Ready）
* 仅仅是**提示**，调度器可以忽略
* 让出后，只有优先级 >= 当前线程的其他线程才有机会获得 CPU

#### 关键 API 签名

```java
public static native void yield();
```

#### 代码示例

```java
// 在循环中适度让步，减少 CPU 空转
while (!done) {
    if (hasWork()) {
        process();
    } else {
        Thread.yield();  // 没有工作时让出 CPU
    }
}
```

### 三者对比

| 特性 | sleep() | join() | yield() |
| :--- | :--- | :--- | :--- |
| 作用 | 暂停当前线程 | 等待目标线程完成 | 让出 CPU 时间片 |
| 目标线程 | 当前线程 | 其他线程 | 当前线程 |
| 释放锁 | 不释放 | 不释放（底层是 wait，会在目标线程对象上等待） | 不释放 |
| 进入状态 | TIMED\_WAITING | WAITING / TIMED\_WAITING | RUNNABLE（Ready） |
| 精确性 | 相对精确 | 依赖目标线程 | 完全不可控 |

### 常见陷阱

* `sleep(0)` 不会真正休眠，但会触发线程调度（类似 yield）
* `join()` 在目标线程尚未启动时立即返回
* `yield()` 在实际应用中很少使用，可读性差且效果不可控

### 最佳实践

* 使用 `TimeUnit.MILLISECONDS.sleep(500)` 替代 `Thread.sleep(500)`，可读性更好
* `join()` 应始终设置超时时间，防止死等
* 优先使用 `java.util.concurrent` 包中的高级工具（如 `CountDownLatch`、`CompletableFuture`）替代手动的 `join()` 和 `sleep()`

***

## 9. Object.wait()、Object.notify()、Object.notifyAll()

### 核心概念

* 这三个方法是 `Object` 类的 `final native` 方法，任何对象都可以作为同步监视器
* **必须在 synchronized 块内调用**，且调用者必须是当前持有锁的对象（即同步监视器），否则抛出 `IllegalMonitorStateException`
* `wait()` 会**释放锁**并进入等待池；`notify()` / `notifyAll()` 唤醒等待池中的线程进入锁池重新竞争锁

### 关键 API 签名

```java
// Object 类方法
public final native void wait(long timeout) throws InterruptedException;
public final void wait(long timeout, int nanos) throws InterruptedException;
public final native void notify();
public final native void notifyAll();
```

### 等待/通知机制的标准范式

```java
// ====== 消费者（等待方） ======
synchronized (lock) {
    while (!condition) {          // 必须用 while，不能用 if（防止虚假唤醒）
        lock.wait();              // 释放锁，进入等待
    }
    // 条件满足，执行操作
    doSomething();
}

// ====== 生产者（通知方） ======
synchronized (lock) {
    changeState();                // 改变条件
    lock.notifyAll();             // 唤醒等待线程（推荐 notifyAll）
}
```

### 经典示例：生产者-消费者

```java
public class BoundedBuffer<T> {
    private final Object[] items;
    private int putIdx, takeIdx, count;
    private final Object lock = new Object();

    public BoundedBuffer(int capacity) {
        items = new Object[capacity];
    }

    @SuppressWarnings("unchecked")
    public T take() throws InterruptedException {
        synchronized (lock) {
            while (count == 0) {
                lock.wait();   // 缓冲区空，等待
            }
            T item = (T) items[takeIdx];
            takeIdx = (takeIdx + 1) % items.length;
            count--;
            lock.notifyAll();  // 通知生产者可以放入
            return item;
        }
    }

    public void put(T item) throws InterruptedException {
        synchronized (lock) {
            while (count == items.length) {
                lock.wait();   // 缓冲区满，等待
            }
            items[putIdx] = item;
            putIdx = (putIdx + 1) % items.length;
            count++;
            lock.notifyAll();  // 通知消费者可以取出
        }
    }
}
```

### wait() 的释放锁规则

| 操作 | 是否释放 synchronized 锁 |
| :--- | :--- |
| `wait()` | **释放**（进入等待池） |
| `sleep()` | 不释放 |
| `yield()` | 不释放 |
| `suspend()` (已废弃) | 不释放 |

### 常见陷阱

* **虚假唤醒（Spurious Wakeup）**：线程可能在没有被 `notify()` 的情况下从 `wait()` 中返回，因此**必须使用 `while` 循环检查条件**，而不是 `if`
* 在错误的锁对象上调用 `wait()` / `notify()` 会抛出 `IllegalMonitorStateException`
* `notify()` 唤醒的是等待池中的**任意一个**线程（不保证公平性），可能导致某些线程永远得不到唤醒（信号丢失问题）
* `wait(0)` 等同于 `wait()`（无限期等待）

### 最佳实践

* 始终使用 `notifyAll()` 而非 `notify()`，除非你明确知道只有一个等待线程且条件精确匹配
* 始终使用 `while` 循环保护 `wait()` 调用
* 推荐使用 `java.util.concurrent` 包中的高级工具替代手写 wait/notify：
  * `BlockingQueue` 替代生产者-消费者模式
  * `Condition` 接口（配合 `Lock`）替代 `wait()/notify()`
  * `CountDownLatch` / `CyclicBarrier` 替代线程间的等待/通知

***

## 总结：线程协作工具选型指南

| 需求场景 | 推荐方案 |
| :--- | :--- |
| 线程间简单等待/通知 | `synchronized` + `wait()` / `notifyAll()` |
| 精确的线程间等待/通知 | `Lock` + `Condition` |
| 生产者-消费者模式 | `BlockingQueue` |
| 等待一组线程完成 | `CountDownLatch` 或 `CompletableFuture` |
| 线程间屏障同步 | `CyclicBarrier` |
| 限流 / 并发数控制 | `Semaphore` |
| 异步计算 | `CompletableFuture` |
| 定时/周期任务 | `ScheduledExecutorService` |

***

> 参考链接：
>
> * [The Java Tutorials - Concurrency](https://docs.oracle.com/javase/tutorial/essential/concurrency/)
> * [Thread.State JDK 17 源码](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Thread.State.html)
> * [Java 并发编程实战](https://book.douban.com/subject/10484692/)
