「这是我参与2022首次更文挑战的第3天,活动详情查看:2022首次更文挑战」。
生命周期模型
线程的生命周期即线程在多种状态间转换的过程,可以用五种状态的模型来表示
-
初始化状态
线程在编程语言层面已被创建,而在操作系统层面还未创建真正的线程,如 Java 中还没有调用线程 start 方法
-
就绪状态
操作系统层面的线程已经被创建,等待 CPU 调度执行
-
运行状态
线程获取到 CPU 执行权限,开始执行线程的内容代码
-
休眠状态
线程调用了阻塞式的 API 或者等待某个事件,就会进入休眠状态,释放 CPU 资源
当等待的事件出现,线程回归就绪状态等待调度
-
死亡状态
线程执行完毕,生命周期结束
Java 线程状态
-
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 的状态转换
- 转换到 WAITING 的各种方式中,如果使用了超时参数,就是 TIMED_WAITING
- 调用了带超时参数的 sleep 方法
-
RUNNABLE 到 TERMINATED 的状态转换
-
run 方法执行完毕或抛出异常
-
调用了中断线程的方法,如 stop(不推荐使用)
-
中断线程的方式
-
使用强制中断的 API
stop、suspend 方法已经标记为 @Deprecated,不推荐使用
-
使用 interrupt 方法通知线程
当线程内使用了 wait、join、sleep、lockInterruptibly(lock 锁) 这种可以抛出 InterruptedException 异常的 API,调用该线程的 interrupt 方法,就会触发线程内抛出 InterruptedException 异常
也可以在线程内通过 isInterrupted 方法检查是否被中断,需要注意的是,在捕获异常后中断标记被自动清除,可以使用
Thread.currentThread().interrupt()重新标记