线程的生命周期即线程在多种状态间转换的过程,可以用五种状态的模型来表示
初始化状态
线程在编程语言层面已被创建,而在操作系统层面还未创建真正的线程,如 Java 中还没有调用线程 start 方法
就绪状态
操作系统层面的线程已经被创建,等待 CPU 调度执行
运行状态
线程获取到 CPU 执行权限,开始执行线程的内容代码
休眠状态
线程调用了阻塞式的 API 或者等待某个事件,就会进入休眠状态,释放 CPU 资源
当等待的事件出现,线程回归就绪状态等待调度
死亡状态
线程执行完毕,生命周期结束
Java Thread 内部类 State 中,规定了 Java 线程的几种状态
public enum State {
// 初始化状态
NEW,
// 就绪 / 运行状态
RUNNABLE,
// 阻塞状态
BLOCKED,
// 无限等待
WAITING,
// 有时限等待
TIMED_WAITING,
// 死亡状态
TERMINATED;
}
就绪状态和运行状态统一归为 RUNNABLE,而 BLOCKED、WAITING、TIMED_WAITING 都是休眠状态的一种
需要注意的是调用阻塞式 API,如 IO 阻塞,在操作系统层面表现为休眠状态,但在 Java 虚拟机层面状态是 RUNNABLE
NEW 与 RUNNABLE 的状态转换
调用 start 方法
Thread t = new Thread(() -> System.out.println(Thread.currentThread().getState()));
System.out.println(t.getState());
t.start();
// NEW
// RUNNABLE
RUNNABLE 与 BLOCKED 的状态转换
当执行中的线程等待 synchronized 锁时,状态变为 BLOCKED
Object o = new Object();
// 线程 1 获取锁睡 3 秒
Thread t1 = new Thread(() -> {
synchronized (o){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
Thread t2 = new Thread(() -> {
synchronized (o){
System.out.println("t2 run");
}
});
t2.start();
// 主线程睡 1 秒,用来打印线程 2 没有获取锁的状态
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(t2.getState()); // BLOCKED
RUNNABLE 与 WAITING 的状态转换
获取 synchronized 锁的线程,调用了 wait 方法,notify 后变为 RUNNABLE
线程 A 中调用了线程 B 的 join 方法,线程 A 变为 WAITING 状态,线程 B 执行完毕后线程 A 变为 RUNNABLE
LockSupport 的 park 方法是 JDK 并发包中实现锁的基础,会使线程变为 WAITING 状态,调用 unpark 方法后会变为 RUNNABLE
RUNNABLE 与 TIMED_WAITING 的状态转换
RUNNABLE 到 TERMINATED 的状态转换
run 方法执行完毕或抛出异常
调用了中断线程的方法,如 stop(不推荐使用)
使用强制中断的 API
stop、suspend 方法已经标记为 @Deprecated,不推荐使用
使用 interrupt 方法通知线程
当线程内使用了 wait、join、sleep、lockInterruptibly(lock 锁) 这种可以抛出 InterruptedException 异常的 API,调用该线程的 interrupt 方法,就会触发线程内抛出 InterruptedException 异常
也可以在线程内通过 isInterrupted 方法检查是否被中断,需要注意的是,在捕获异常后中断标记被自动清除,可以使用 Thread.currentThread().interrupt()
重新标记