文章首发自个人微信公众号: 小哈学Java

https://www.exception.site/java-concurrency/java-concurrency-thread-life-cycle

在 Java 初中级面试中,关于线程的生命周期可以说是常客了。本文就针对这个问题,通过图文并茂的方式详细说说。

结合上图,线程的生命周期大致可分为以下五种状态

  • NEW - 新建
  • RUNNABLE - 等待被CPU调度
  • RUNNING - 正在运行
  • BLOCKED - 阻塞
  • TERMINATED - 结束

一、NEW 状态

NEW 状态表示线程被新建的状态,我们来看一段示例代码:

Thread thread = new Thread(() -> System.out.println("Hello, world !"));

当我们在代码中 new 一个 Thread 的时候,就代表着 thread 线程处于 NEW 状态了,但是此时该线程还未被操作系统创建出来,只有当我们调用了 start() 方法之后,该线程才会被创建出来。所以准确来说,NEW 状态只是线程对象的状态。


NEW 状态的线程能发生哪些状态转变?

NEW 状态的线程在调用 start() 方法后,进入 RUNNABLE 状态。

二、RUNNABLE 状态

当我们在代码中显式的调用 start() 方法后,JVM 进程会去创建一个新的线程,而此线程不会马上被 CPU 调度运行,进入 RUNNING 状态,这里会有一个中间状态,就是 RUNNABLE 状态,你可以理解为等待被 CPU 调度的状态:

如上图所示,也就是说线程会从 NEW 状态 -> RUNNABLE 状态 ,等待 CPU 调度,再大白话一点,就是说这种线程具备被执行的资格,但是能否进入进行阶段,还得看 CPU 的脸色说话。


RUNNABLE 状态的线程能发生哪些状态转变?

RUNNABLE 状态的线程无法直接进入 BLOCKED 状态和 TERMINATED 状态的。

很多小伙伴这里可能有疑问,为啥呢?

只有处在 RUNNING 状态的线程,换句话说,只有获得 CPU 调度执行权的线程才有资格进入 BLOCKED 状态和 TERMINATED 状态

PS: RUNNABLE 状态的线程要么能被转换成 RUNNING 状态,要么被意外终止(如 kill -9 PID)。

三、RUNNING 状态

当 CPU 调度发生,并任务队列中选中了某个 RUNNABLE 线程时,该线程会进入 RUNNING 执行状态,并且开始调用 run()方法中逻辑代码。


RUNNING 状态的线程能发生哪些状态转变?

  • 被转换成 TERMINATED 状态,比如调用 stop() 方法;
  • 被转换成 BLOCKED 状态,比如调用了sleep, wait 方法被加入 waitSet 中;
  • 被转换成 BLOCKED 状态,如进行 IO 阻塞操作,如查询数据库进入阻塞状态;
  • 被转换成 BLOCKED 状态,比如获取某个锁的释放,而被加入该锁的阻塞队列中;
  • 该线程的时间片用完,CPU 再次调度,进入 RUNNABLE 状态;
  • 线程主动调用 yield 方法,让出 CPU 资源,进入 RUNNABLE 状态;

四、BLOCKED 状态

上小节中我们已经讲到了,进入 BLOCKED 原因,这里,我们就直接谈谈 BLOCK 状态的线程能够发生哪些状态改变:

  • 被转换成 TERMINATED 状态,比如调用 stop() 方法,或者是 JVM 意外 Crash;
  • 被转换成 RUNNABLE 状态,阻塞时间结束,比如读取到了数据库的数据后;
  • 完成了指定时间的休眠,进入到 RUNNABLE 状态;
  • 正在 wait 中的线程,被其他线程调用 notify/notifyAll 方法唤醒,进入到 RUNNABLE 状态;
  • 线程获取到了想要的锁资源,进入 RUNNABLE 状态;
  • 线程在阻塞状态下被打断,如其他线程调用了 interrupt 方法,进入到 RUNNABLE 状态;

五、TERMINATED 状态

TERMINATED 状态是线程的最终状态,处于此状态中的线程不会切换到以上任何状态,一旦线程进入了 TERMINATED 状态,就意味着这个线程生命的终结,没有回头路了。

以下情况下,线程会进入到 TERMINATED 状态:

  • 线程正常运行结束,生命周期结束;
  • 线程运行过程中出现意外错误;
  • JVM 异常结束,所有的线程生命周期均被结束。

六、start 方法源码解析,何时调用的 run() 方法?

通过图文,我们了解了线程生命周期的五种状态,接下来,我们来看看 start 方法源码,其实内部的源码非常简单,如下图所示:

  • :首先,会判断线程的状态是否是 NEW 状态,内部对应的状态标识是个 0,也就是说如果不等于 0,直接抛线程状态异常;
  • :线程在启动后被加入到 ThreadGroup 中;
  • : start0 是最核心的方法了,就是运行状态为 NEW (内部状态标识为 0) 的线程;
  • start0 是个 native 方法,也就是 JNI 方法;

看到这里,你也许会有个疑问,自己重写的 run 方法是什么时候被调用的呢?源码中也没看到调用啊!!

Causes this thread to begin execution; the Java Virtual Machine calls the run method of this thread.

上面这段截自 JDK 官方文档,意思是说:

run 方法是在调用 JNI 方法 start0() 的时候被调用的,被调用后,我们写的逻辑代码才得以被执行。

一些面试中,面试官也会经常问到这个问题:线程的 start 方法和 run 方法有什么区别?

相信看完上面的源码分析,小伙伴们一定可以源码的角度怼回去了!

七、总结

本文中,小哈通过图文的方式解释了线程的五种状态,以及各种状态能够被转换的状态。最后,我们简单看了一下 start()内部源码,知道了 run() 方法何时被执行的。最后,希望看完本文的小伙伴们能有所收获,下期见!

八、Ref

  • 《Java高并发编程详解》

欢迎关注微信公众号: 小哈学Java

赠送 | 面试&学习福利资源

最近在网上发现一个不错的 PDF 资源《Java 核心面试知识.pdf》分享给大家,不光是面试,学习,你都值得拥有!!!

获取方式: 关注微信公众号: 小哈学Java, 后台回复"资源",既可无套路获取资源链接,下面是目录以及部分截图:

重要的事情说两遍,获取方式: 关注微信公众号: 小哈学Java, 后台回复"资源",既可无套路获取资源链接 !!!

欢迎关注微信公众号: 小哈学Java

图解Java线程的生命周期,看完再也不怕面试官问了的更多相关文章

  1. 【Java并发基础】Java线程的生命周期

    前言 线程是操作系统中的一个概念,支持多线程的语言都是对OS中的线程进行了封装.要学好线程,就要搞清除它的生命周期,也就是生命周期各个节点的状态转换机制.不同的开发语言对操作系统中的线程进行了不同的封 ...

  2. Java线程的生命周期(转)

    Java线程的生命周期 一个线程的产生是从我们调用了start方法开始进入Runnable状态,即可以被调度运行状态,并没有真正开始运行,调度器可以将CPU分配给它,使线程进入Running状态,真正 ...

  3. 面试问烂的 MySQL 查询优化,看完屌打面试官!

    Java技术栈 ,一般把连接数设置得大一些). 并发量:同一时刻数据库服务器处理的请求数量 3.超高的 CPU使用率:CPU资源耗尽出现宕机. 4.磁盘 IO:磁盘 IO性能突然下降.大量消耗磁盘性能 ...

  4. Spring第三天,详解Bean的生命周期,学会后让面试官无话可说!

    点击下方链接回顾往期 不要再说不会Spring了!Spring第一天,学会进大厂! Spring第二天,你必须知道容器注册组件的几种方式!学废它吊打面试官! 今天讲解Spring中Bean的生命周期. ...

  5. Java线程的生命周期

    线程的生命周期包括:新建(New).就绪(Runnable).运行(Running).阻塞(Blocked)和死亡(Dead)5种状态.线程状态转换图如下: 1.新建状态(New) 当程序使用new关 ...

  6. java线程的生命周期及五种基本状态

    一.线程的生命周期及五种基本状态 关于Java中线程的生命周期,首先看一下下面这张较为经典的图: 上图中基本上囊括了Java中多线程各重要知识点.掌握了上图中的各知识点,Java中的多线程也就基本上掌 ...

  7. Java—线程的生命周期及线程控制方法详解

    线程生命周期5种状态 介绍   线程的生命周期经过新建(New).就绪(Runnable).运行(Running).阻塞(Bolocked)和死亡(Dead) 状态转换图 新建(New)   程序使用 ...

  8. Java 线程的生命周期

    当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态,在线程的生命周期中,它要经过新建(New).就绪(Runnable).运行(Running).阻塞(Blocked)和死 ...

  9. Java线程之生命周期

    简述 以下类图展示了线程生命周期中不同的状态.我们可以创建一个线程并启动它,但是线程状态从Runnable.Running.Blocked等状态的变化取决于系统线程调度器,java本身并不能完全控制. ...

随机推荐

  1. java 操作Excel表格

    对于Excel表格的解析.生成,java在 org.apache.poi 包中已经封装好了,使用比较简单. 解析Excel: 首先将File文件转成InputStream InputStream in ...

  2. C#高级编程笔记之第二章:核心C#

    变量的初始化和作用域 C#的预定义数据类型 流控制 枚举 名称空间 预处理命令 C#编程的推荐规则和约定 变量的初始化和作用域 初始化 C#有两个方法可以一确保变量在使用前进行了初始化: 变量是字段, ...

  3. python2.7 的中文编码处理,解决UnicodeEncodeError: 'ascii' codec can't encode character 问题

    最近业务中需要用 Python 写一些脚本.尽管脚本的交互只是命令行 + 日志输出,但是为了让界面友好些,我还是决定用中文输出日志信息. 很快,我就遇到了异常: UnicodeEncodeError: ...

  4. spring cloud 入门系列七:基于Git存储的分布式配置中心

    我们前面接触到的spring cloud组件都是基于Netflix的组件进行实现的,这次我们来看下spring cloud 团队自己创建的一个全新项目:Spring Cloud Config.它用来为 ...

  5. Spring Boot实战笔记(九)-- Spring高级话题(组合注解与元注解)

    一.组合注解与元注解 从Spring 2开始,为了响应JDK 1.5推出的注解功能,Spring开始大量加入注解来替代xml配置.Spring的注解主要用来配置注入Bean,切面相关配置(@Trans ...

  6. SpringMVC中日期格式的转换

    解决日期提交转换异常的问题 由于日期数据有很多种格式,所以springmvc没办法把字符串转换成日期类型.所以需要自定义参数绑定.前端控制器接收到请求后,找到注解形式的处理器适配器,对RequestM ...

  7. spring配置文件中context:property-placeholder导入多个独立的配置文件

    spring中 context:property-placeholder 导入多个独立的 .properties配置文件? Spring容器采用反射扫描的发现机制,在探测到Spring容器中有一个 o ...

  8. 用react重构个人网站 3-22

    问题一:import React from 'react'这个写法是怎么回事? 答案:require是common.js的写法,import是ES6的写法,主要功能都是引入模块,写法上: var mo ...

  9. 32.APP后端处理表情的一些技巧

    app应用中文字夹带表情是个很常见的现象.甚至一些40多岁的大叔级用户,也喜欢在自己的昵称中夹带表情,在产品运营后发现这个现象,彻底颠覆了我的世界观. 在后台处理表情的时间,我遇到过下面3个问题: 1 ...

  10. 关于基线baseline及与inline-block、vertical-aline等属性的关系(完善中.......)

    1. 基本含义 基线(base line):而是英文字母x的下端沿,是a,c,z,x等字母的底边线,并不是汉字文字的下端沿,.下图的红色线即为基线.凡是涉及到垂直方向的排版或者对齐的,都离不开最最基本 ...