「技术点串烧」☕【Java 技术指南】「难点 - 核心 - 遗漏」Java 线程状态流转及生命周期的技术指南!
前提介绍
本章主要介绍相关线程声明周期的转换机制以及声明周期的流转关系以及相关 AQS 的实现和相关的基本原理,配合这相关官方文档的中英文互译的介绍。
线程状态流转及生命周期
当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中,它要经过新建(New)、就绪/可运行状态(Runnable)、阻塞(Blocked)和等待(Wait)、时间等待(Time_wait)、终止状态(Terminate)六种状态。尤其是当线程启动以后,它不能一直“霸占”着 CPU 独自运行,所以 CPU 需要在多条线程之间切换,于是线程状态也会多次在运行、阻塞之间切换。
下图借鉴于官方网站:
生命周期的六种状态
一个事物从出生的那一刻开始到最终死亡中间的整个过程.在事物的漫长的生命周期过程中,总会经历不同的状态(婴儿状态/青少年状态/中年状态/老年状态...).线程也是有生命周期的,也是存在不同的状态的,状态相互之间的转换。
线程对象的状态存放在 Thread 类的内部类(State)中:
注意:Thread.State 类其实是一个枚举类.因为线程对象的状态是固定的,只有 6 种,此时使用枚举来表示是。
新建(new Thread)
当创建 Thread 类的一个实例(对象)时,此线程进入新建状态(未被启动)。
使用 new 创建一个线程对象,仅仅在堆中分配内存空间,在调用 start 方法之前。 新建状态下,线程压根就没有启动,仅仅只是存在一个线程对象而已.Thread t = new Thread();
此时 t 就属于新建状态当新建状态下的线程对象调用了 start 方法,此时从新建状态进入可运行状态.线程对象的 start 方法只能调用一次,否则报错:IllegalThreadStateException.
例如
可运行(runnable)
分成两种子状态,ready 和 running。分别表示就绪状态和运行状态。
就绪状态
线程对象调用 start 方法之后,等待 JVM 的调度(此时该线程并没有运行),这时候线程处于等待 CPU 分配资源阶段,谁先抢的 CPU 资源,谁开始执行,换句话说线程已经被启动,正在等待被分配给 CPU 时间片,也就是说此时线程正在就绪队列中排队等候得到 CPU 资源。
运行状态
线程对象获得 JVM 调度,如果存在多个 CPU,那么允许多个线程并行运行
被转换成 Terminated 状态,比如调用 stop() 方法;
被转换成 Blocked 状态,比如调用了 sleep, wait 方法被加入 waitSet 中;
被转换成 Blocked 状态,如进行 IO 阻塞操作,如查询数据库进入阻塞状态;
被转换成 Blocked 状态,比如获取某个锁的释放,而被加入该锁的阻塞队列中;
该线程的时间片用完,CPU 再次调度,进入 Runnable 状态;
线程主动调用 yield 方法,让出 CPU 资源,进入 Runnable 状态
例如
运行(running)他也从属于 Runnable 状态,但不在总体状态之内,属于逻辑状态机制
当就绪的线程被调度并获得 CPU 资源时,便进入运行状态,run 方法定义了线程的操作和功能,此时除非此线程自动放弃 CPU 资源或者有优先级更高的线程进入,线程将一直运行到结束。
注意:
Runnable 状态的线程无法直接进入 Blocked 状态和 Terminated 状态的。只有处在 Running 状态的线程,换句话说,只有获得 CPU 调度执行权的线程才有资格进入 Blocked 状态和 Terminated 状态,Runnable 状态的线程要么能被转换成 Running 状态,要么被意外终止。
堵塞(blocked)
正在运行的线程因为某些原因放弃 CPU,暂时停止运行,就会进入阻塞状态.此时 JVM 不会给线程分配 CPU,直到线程重新进入就绪状态,才有机会转到运行状态.阻塞状态只能先进入就绪状态,不能直接进入运行状态。
阻塞状态的两种情况:
当 A 线程处于运行过程时,试图获取同步锁时,却被 B 线程获取.此时 JVM 把当前 A 线程存到对象的锁池中,A 线程进入阻塞状态.
当线程处于运行过程时,发出了 IO 请求时,此时进入阻塞状态.
由于某种原因导致正在运行的线程让出 CPU 并暂停自己的执行,即进入堵塞状态。
被转换成 Terminated 状态,比如调用 stop() 方法,或者是 JVM 意外 Crash;
被转换成 Runnable 状态,阻塞时间结束,比如读取到了数据库的数据后;
完成了指定时间的休眠,进入到 Runnable 状态;
正在 wait 中的线程,被其他线程调用 notify/notifyAll 方法唤醒,进入到 Runnable 状态;
线程获取到了想要的锁资源,进入 Runnable 状态;
线程在阻塞状态下被打断,如其他线程调用了 interrupt 方法,进入到 Runnable 状态;
等待状态(waiting)
(等待状态只能被其他线程唤醒):
此时使用的无参数的 wait 方法,
当线程处于运行过程时,调用了 wait()方法,此时 JVM 把当前线程存在对象等待池中.
计时等待状态(timed waiting)
(使用了带参数的 wait 方法或者 sleep 方法)
当线程处于运行过程时,调用了 wait(long time)方法,此时 JVM 把当前线程存在对象等待池中.
:当前线程执行了 sleep(long time)方法.
终止状态(terminated)
通常称为死亡状态,表示线程终止.
正常执行完 run 方法而退出(正常死亡).
遇到异常而退出(出现异常之后,程序就会中断)(意外死亡).
JVM 异常结束,所有的线程生命周期均被结束。
线程一旦终止,就不能再重启启动,否则报错(IllegalThreadStateException).
不推荐使用的线程方法
在 Thread 类中过时的方法(因为存在线程安全问题,所以弃用了):
void suspend() :暂停当前线程
void resume() :恢复当前线程
void stop() :结束当前线程
给大家结合官网在进行一个中文解释的状态流转图:
版权声明: 本文为 InfoQ 作者【李浩宇/Alex】的原创文章。
原文链接:【http://xie.infoq.cn/article/05fcb133d208812ee52a525d3】。文章转载请联系作者。
评论