面试必答题“聊聊 Java 中线程的生命周期状态”如何破?
“聊聊 Java 中线程的生命周期状态吧!”
这几乎是一道面试必答题,这道题怎么答才是最佳答案呢?本文就带大家来破解一下!
01
一张图说明线程生命周期
JVM 源码中将线程的生命周期分为新建(New)、可运行(Runnable)、阻塞(Blocked)、等待(Waiting)、超时等待(Timed_Waiting)和终止(Terminated)这 6 种状态。
在系统运行过程中不断有新的线程被创建,老的线程在执行完毕后被清理,线程在排队获取共享资源或者锁时将被阻塞,因此运行中的线程会在可运行、阻塞、等待状态之间来回切换。
线程的具体状态转化流程如图所示。
02
线程生命周期状态转化流程
(1)调用 new 方法新建一个线程,这时线程处于新建状态。
(2)调用 start 方法启动一个线程,这时线程处于可运行状态。可运行状态中又分:就绪(Ready)和运行中(Running)两种状态。处于就绪状态的线程等待线程获取 CPU 资源,在等待其获取 CPU 资源后线程会执行 run 方法进入运行中状态;正在运行的线程在调用了 yield 方法或失去处理器资源时,会再次进入就绪状态。
(3)正在运行中的线程在执行了 sleep 方法、I/O 阻塞、等待同步锁、等待通知、调用 suspend 方法等操作后,会挂起并进入阻塞状态。阻塞状态的线程由于出现 sleep 时间已到、I/O 方法返回、获得同步锁、收到通知、调用 resume 方法等情况,会再次进入可运行状态中的就绪状态,等待 CPU 时间片的轮询。该线程在获取 CPU 资源后,会再次进入运行状态。
(4)当线程调用了 Object.wait ()、Object.join()、LockSupport.park()后线程进入等待状态。等待状态的线程调用 Object.notify ()、Object. notifyAll()、LockSupport.unpark(Thread)方法后会再次进入可运行状态。
(5)当可运行状态的线程调用 Thread.sleep(long)、Object.wait(long)、Thread.join(long)、LockSupport.parkNanos()、LockSupport.parkUntil()时线程会进入超时等待状态。当超时等待的线程出现超时时间到、等待进入 synchronized 方法、等待进入 synchronized 块或者调用 Object.notify ()、Object. notifyAll()、LockSupport.unpark(Thread)时会再次进入可运行状态。
(6)处于可运行状态的线程,在调用 run 方法或 call 方法正常执行完成、调用 stop 方法停止线程或者程序执行错误导致异常退出时,会进入终止状态。
03
线程生命周期详解
新建状态(New)
在 Java 中使用 new 关键字创建一个线程,新创建的线程将处于新建状态。在创建线程时主要是为线程分配内存并初始化其成员变量的值。
就绪状态:Runnable
新建的线程对象在调用 start 方法之后将转为可运行状态。运行状态中又分:就绪(Ready)和运行中(Running)两种状态。就绪状态指的是 JVM 完成了方法调用栈和程序计数器的创建,等待该线程的调度和运行。
就绪状态的线程在竞争到 CPU 的使用权并开始执行 run 方法的线程执行体时,会转为运行中状态,处于运行中状态的线程的主要任务就是执行 run 方法中的逻辑代码。
阻塞状态:Blocked
运行中的线程会主动或被动地放弃 CPU 的使用权并暂停运行,此时该线程将转为阻塞状态,直到再次进入可运行状态,才有机会再次竞争到 CPU 使用权并转为运行状态。阻塞状态分为如下三种。
(1)等待阻塞:在运行状态的线程调用 o.wait 方法时,JVM 会把该线程放入等待队列(Waitting Queue)中,线程转为阻塞状态。
(2)同步阻塞:在运行状态的线程尝试获取正在被其他线程占用的对象同步锁时,JVM 会把该线程放入锁池(Lock Pool)中,此时线程转为阻塞状态。
(3)其他阻塞:运行状态的线程在执行 Thread.sleep(long ms)、Thread.join()或者发出 I/O 请求时,JVM 会把该线程转为阻塞状态。直到 sleep()状态超时、Thread.join()等待线程终止或超时,或者 I/O 处理完毕,线程才重新转为可运行状态。
等待状态:Waiting
当线程调用了 Object.wait()、Thread.join()、LockSupport.park()会进入等待状态。处于等待状态的线程正在等待另一个线程执行指定的操作。例如,调用 Object.wait()的一个线程对象正在等待另一个线程调用该对象的 Object.notify()或 Object.notifyAll()。调用 thread .join()的线程正在等待指定的线程退出。
超时等待状态:Timed_Waiting
超时等待和等待状态的不同是,超时等待状态的线程经过超时时间后会自动唤醒。当线程调用了 Thread.sleep ()、Object.wait(long)、Thread.join(long)、LockSupport.parkNanos()、LockSupport.parkUntil()后线程会进入超时等待状态。
线程终止:Terminated
线程在以如下三种方式结束后转为终止状态。
◎ 线程正常结束:run 方法或 call 方法执行完成。
◎ 线程异常退出:运行中的线程抛出一个 Error 或未捕获的 Exception,线程异常退出。
◎ 手动结束:调用线程对象的 stop 方法手动结束运行中的线程(该方式会瞬间释放线程占用的同步对象锁,导致锁混乱和死锁,不推荐使用)。
本文节选自《Offer 来了:Java 面试核心知识点精讲(第 2 版)》!
王磊 著
超强 Java 面试宝典
超详尽 Java 知识点速查手册
Java 面试官的超趁手 Java 题库参考书
一书在手,Java 我有
本书讲解 Java 面试中常被问及的核心知识点,涉及 Java 基础、Java 并发编程、JVM、Java 高并发网络编程、Spring 基础、Netflix 的原理及应用、Spring Cloud Alibab 的原理及应用、数据结构、Java 中常用算法的原理及其 Java 实现、关系数据库及分布式事务、分布式缓存的原理及应用、ZooKeeper 的原理及应用、Kafka 的原理及应用、Elasticsearch 的原理及应用、设计模式的概念及其 Java 实现。
本书内容全面、细致,既可帮助读者迅速查找 Java 知识点,也可帮助读者完善其 Java 知识体系;不但可以作为 Java 面试知识速通手册,也可以作为 Java 程序员的案头手册。
评论