线程生命周期
1. 线程生命周期(6 种状态)
Section titled “1. 线程生命周期(6 种状态)”Java 线程状态由 Thread.State 枚举定义:
| 状态 | 触发条件 | 退出条件 |
|---|---|---|
1. NEW | new Thread() 后、start() 前 | 调用 start() |
2. RUNNABLE | start() 之后,包含 Ready 和 Running | run() 结束或进入等待 |
3. BLOCKED | 竞争 synchronized 锁失败 | 成功获得锁 |
4. WAITING | wait() / join() / LockSupport.park() | notify() / notifyAll() / unpark() |
5. TIMED_WAITING | sleep(n) / wait(n) / join(n) | 超时或被唤醒 |
6. TERMINATED | run() 方法执行完毕或异常退出 | 终态,不可逆 |
1.1. 关键状态转换说明
Section titled “1.1. 关键状态转换说明”-
NEW → RUNNABLE:调用start(),JVM 向操作系统申请创建线程; -
RUNNABLE → BLOCKED:进入synchronized块/方法时锁被其他线程持有; -
RUNNABLE → WAITING:调用Object.wait()、Thread.join()、LockSupport.park(); -
RUNNABLE → TIMED_WAITING:调用Thread.sleep(n)、Object.wait(n)、Thread.join(n); -
WAITING / TIMED_WAITING → RUNNABLE:被唤醒后不是直接运行,而是先重新竞争锁,竞争成功才回到 RUNNABLE; -
RUNNABLE → TERMINATED:run()正常返回或抛出未捕获异常。
2. Thread 常用 API
Section titled “2. Thread 常用 API”2.1. sleep() vs wait()
Section titled “2.1. sleep() vs wait()”| 对比项 | Thread.sleep(n) | Object.wait() |
|---|---|---|
| 所属类 | Thread | Object |
| 是否释放锁 | 不释放 | 释放 |
| 唤醒方式 | 超时自动醒 | 需要 notify() |
| 使用前提 | 无 | 必须持有对象锁(在 synchronized 块内) |
| 状态 | TIMED_WAITING | WAITING |
2.2. join() — 等待线程结束
Section titled “2.2. join() — 等待线程结束”join() 让当前线程阻塞,直到目标线程执行完毕。常用于主线程等待子线程结果。
Thread t = new Thread(() -> { System.out.println("子线程开始"); // 模拟耗时操作 try { Thread.sleep(1000); } catch (InterruptedException e) {} System.out.println("子线程结束");});t.start();t.join(); // 主线程阻塞,等待 t 执行完毕System.out.println("主线程继续"); // 保证在子线程结束后才执行也可以设置超时,超时后不再等待:
t.join(500); // 最多等待 500ms2.3. interrupt() — 中断线程
Section titled “2.3. interrupt() — 中断线程”Thread.currentThread() // 获取当前线程thread.getName() / setName()thread.getPriority() / setPriority(1~10) // 优先级,仅建议性thread.isDaemon() / setDaemon(true) // 守护线程,JVM 退出时不等待thread.interrupt() // 中断线程(设置中断标志位)Thread.interrupted() // 检查并清除中断标志(静态方法)thread.isInterrupted() // 检查中断标志(不清除)thread.join() // 等待该线程执行完毕Thread.yield() // 让出 CPU,重新竞争(很少用)3. 守护线程
Section titled “3. 守护线程”Thread t = new Thread(() -> { while (true) { /* 后台任务 */ }});t.setDaemon(true); // 必须在 start() 之前设置t.start();-
JVM 中所有非守护线程结束时,JVM 会直接退出,不等守护线程完成;
-
典型守护线程:GC 线程、心跳检测线程。
4. 面试高频问题
Section titled “4. 面试高频问题”Q:线程和进程的区别?
A:进程是操作系统资源分配的基本单位,有独立的地址空间;线程是 CPU 调度的基本单位,共享进程的堆内存和方法区,但有各自的栈和程序计数器。线程切换开销远小于进程切换。
Q:Java 线程与操作系统线程是什么关系?
A:Java 线程(JDK 21 之前)是 1:1 映射到内核线程(KLT),由 OS 调度。JDK 21 引入虚拟线程,是 M:N 映射,由 JVM 调度,可以创建数百万个。
Q:线程可以重复 start() 吗?
A:不能。线程一旦进入 TERMINATED 状态就无法重启,再次调用 start() 会抛出 IllegalThreadStateException。
Q:sleep() 和 wait() 的区别?
A:sleep() 属于 Thread 类,不释放锁,超时自动醒,无需持有锁;wait() 属于 Object 类,调用时必须持有对象锁,调用后释放锁进入等待,需要 notify() 唤醒。
Q:WAITING 状态的线程被 notify() 后直接变成 RUNNABLE 吗?
A:不是。notify() 只是将线程从等待队列移到锁的竞争队列,线程还需要重新竞争 synchronized 锁,竞争成功才真正回到 RUNNABLE。