15Java多线程之生命周期和状态

线程生命周期

image-20210709100121549

img

线程状态

Java 实例 - 获取线程状态 - 菜鸟

线程的状态转换图及常见执行情况(图片仅供参考,详细状态见后文):

image-20210709100027532

img

Java 线程的生命周期中,在 Thread 类里有一个枚举类型 State,定义了线程的几种状态,分别有:

Thread.State.NEW;
Thread.State.RUNNABLE;
Thread.State.BLOCKED;
Thread.State.WAITING;
Thread.State.TIMED_WAITING;
Thread.State.TERMINATED;

初始/新建状态 - NEW

Thread.State.NEW :尚未启动的线程的线程状态。

实现 Runnable 接口和继承 Thread 可以得到一个线程类,new 一个实例出来,线程就进入了初始状态。它保持这个状态直到程序 start() 这个线程。

RUNNABLE

Thread.State.RUNNABLE :可运行线程的线程状态,包括两种状态:

就绪状态(可执行)

简言之,无执行权但可执行。

就绪状态只是说你资格运行,调度程序没有挑选到你,你就永远是就绪状态,正在等待CPU资源。

调用线程的 start() 方法,此线程进入就绪状态。就绪状态的线程处于就绪队列中。

当前线程 sleep() 方法结束,其他线程 join() 结束,等待用户输入完毕,某个线程拿到对象锁,这些线程也将进入就绪状态。

当前线程时间片用完了,调用当前线程的 yield() 方法,当前线程进入就绪状态。

锁池里的线程拿到对象锁后,进入就绪状态。

运行状态(正在执行)

简言之,有执行权且正在执行。

线程调度程序(JVM或操作系统的线程调度器)从可运行池(处于就绪状态的线程)中选择一个线程执行,即就绪状态的线程获取到CPU资源,此时线程就由就绪变成运行。这也是线程进入运行状态的唯一方式。

处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。

阻塞状态

同步阻塞 - BLOCKED

Thread.State.BLOCKED :线程被阻塞,等待监视器锁的线程状态。触发条件:synchronized、Lock.lock。

因为线程获取 synchronized 同步锁失败(同步锁被其他线程占用),所以线程阻塞在进入 synchronized 关键字修饰的代码块或方法(在努力获取锁/争夺锁),此时的状态为 BLOCKED调用 Object.notify 后重新进入重新获取锁。

等待阻塞

等待 - WAITING

Thread.State.WAITING :等待状态

调用以下方法之一,线程处于等待状态:

  • Object.wait 调用无参方法
  • Thread.join 调用无参方法
  • LockSupport.park
  • Condition.await()

处于这种状态的线程不会被分配 CPU 资源,需主动唤醒,否则会处于无限等待状态。例如:

  • 在对象上调用 Object.wait() 的线程正在等待另一个线程在该对象上调用 Object.notify()Object.notifyAll()
  • LockSupport.unpark 已启动的线程, 如果线程在park上被阻塞,那么它将解除阻塞。 否则,保证它的下一次park调用不会阻塞。
  • 调用 Thread.join() 线程正在等待指定的线程终止

Object.wait() 能否使用 LockSupport.unpark 解除阻塞?LockSupport.park 能否使用 Object.notify() 唤醒?

定时等待 - TIMED_WAITING

Thread.State.TIMED_WAITING :具有指定等待时间的等待状态

调用以下方法之一,线程处于定时等待状态:

  • Thread.sleep
  • Object.wait 调用有参方法
  • Thread.join 调用有参方法
  • LockSupport.parkNanos
  • LockSupport.parkUntil
  • Condition.await() 调用有参方法

处于这种状态的线程不会被分配 CPU 执行时间,如果不主动唤醒,那么一定时间后它们会自动唤醒

其他阻塞

发出 I/O 请求时,线程也会进入阻塞状态。但不知实际对应哪一种( Thread.State.BLOCKED ) ?I/O 处理完毕,重新转为就绪状态。

终止/死亡状态 - TERMINATED

Thread.State.TERMINATED :线程已完成执行或被终止

如何终止正在运行的线程:

  • 主线程 main() 方法完成即终止。
  • 线程的 run() 方法完成即终止。
  • 终止条件发生。调用 stop()、interrupt() 方法