这么说线程生命周期是不是简单了点?
点击上方蓝色“程序猿DD”,选择“设为星标”
回复“资源”获取独家整理的学习资料!
来源 | 公众号「日拱一兵」
为什么要了解线程的生命周期?
之前写过 Spring Bean 生命周期三部曲:
Spring Bean生命周期之缘起
Spring Bean生命周期之缘尽
Spring Aware 到底是什么?
有朋友留言说:“了解了它们的生命周期后,使用 Spring Bean 好比看到它们的行动轨迹,现在使用就一点都不慌了”。我和他一样,了解事物的生命周期目的很简单,唯【不慌】也
Java 并发系列 已经写了很多,从来还没提起过那个它【Java线程生命周期】。有了前序理论图文的铺垫,在走进源码世界之前,谈论它的时机恰好到了。因为,编写并发程序的核心之一就是正确的摆弄线程状态
线程生命周期的几种状态
刚接触线程生命周期时,我总是记不住,也理解不了他们的状态,可以说是比较混乱,更别说它们之间是如何进行状态转换的了。原因是我把操作系统通用线程状态
和编程语言封装后的线程状态
概念混淆在一起了
操作系统通用线程状态
个人觉得通用线程状态更符合我们的思考习惯。其状态总共有 5 种 (如下图)。对于经常写并发程序的同学来说,其嘴里经常念的都是操作系统中的这些通用线程状态,且看
除去生【初始状态】死【终止状态】,其实只是三种状态的各种转换,听到这句话是不是心情放松了很多呢?
为了更好的说明通用线程状态
和 Java 语言中的线程状态
,这里还是先对前者进行简短的说明
初始状态
线程已被创建,但是还不被允许分配CPU执行。注意,这个被创建其实是属于编程语言层面的,实际在操作系统里,真正的线程还没被创建, 比如 Java 语言中的 new Thread()。
可运行状态
线程可以分配CPU执行,这时,操作系统中线程已经被创建成功了
运行状态
操作系统会为处在可运行状态的线程
分配CPU时间片,被 CPU 临幸后,处在可运行状态的线程就会变为运行状态
休眠状态
如果处在运行状态的线程调用某个阻塞的API
或等待某个事件条件可用
,那么线程就会转换到休眠状态,注意:此时线程会释放CPU使用权,休眠的线程永远没有机会获得CPU使用权,只有当等待事件出现后,线程会从休眠状态转换到可运行状态
终止状态
线程执行完
或者出现异常
(被interrupt那种不算的哈,后续会说)就会进入终止状态,正式走到生命的尽头,没有起死回生的机会
接下来就来看看你熟悉又陌生,面试又经常被问到的Java 线程生命周期吧
Java语言线程状态
在 Thread 的源码中,定义了一个枚举类 State,里面清晰明了的写了Java语言中线程的6种状态:
NEW
RUNNABLE
BLOCKED
WAITING
TIMED_WAITING
TERMINATED
耳边响起五环之歌,Java中线程状态竟然比通用线程状态的 5 种多1种,变成了 6 种。这个看似复杂,其实并不是你想的那样,Java在通用线程状态的基础上,有裁剪,也有丰富,整体来说是少一种。再来看个图,注意颜色区分哦
Java 语言中
将通用线程状态的
可运行状态
和运行状态
合并为Runnable
,将
休眠状态
细分为三种 (BLOCKED
/WAITING
/TIMED_WAITING
); 反过来理解这句话,就是这三种状态在操作系统的眼中都是休眠状态,同样不会获得CPU使用权
看上图右侧【Java语言中的线程状态】,进一步简洁的说,除去线程生死,我们只要玩转 RUNNABLE
和休眠状态
的转换就可以了,编写并发程序也多数是这两种状态的转换。所以我们需要了解,有哪些时机,会触发这些状态转换
远看看轮廓, 近看看细节。我们将上面Java语言中的图进行细化,将触发的节点放到图中 (这看似复杂的图,其实三句话就能分解的,所以别慌),且看:
RUNNABLE与BLOCKED状态转换
当且仅有(just only)一种情况会从 RUNNABLE 状态进入到 BLOCKED 状态,就是线程在等待 synchronized 内置隐式锁;如果等待的线程获取到了 synchronized 内置隐式锁,也就会从 BLOCKED 状态变为 RUNNABLE 状态了
注意:
上面提到,以操作系统通用状态来看,线程调用阻塞式 API,会变为休眠状态(释放CPU使用权),但在JVM层面,Java线程状态不会发生变化,也就是说Java线程的状态依旧会保持在 RUNNABLE 状态。JVM并不关心操作系统调度的状态。在JVM看来,等待CPU使用权(操作系统里是处在可执行状态)与等待I/O(操作系统是处在休眠状态),都是等待某个资源,所以都归入了RUNNABLE 状态
—— 摘自《Java并发编程实战》
RUNNABLE与WAITING状态转换
调用不带时间参数的等待API,就会从RUNNABLE状态进入到WAITING状态;当被唤醒就会从WAITING进入RUNNABLE状态
RUNNABLE与 TIMED-WAITING 状态转换
调用带时间参数的等待API,自然就从 RUNNABLE 状态进入 TIMED-WAITING 状态;当被唤醒或超时时间到就会从TIMED_WAITING进入RUNNABLE状态
看图中的转换 API 挺多的,其实不用担心,后续分析源码章节,自然就会记住的,现在有个印象以及知道状态转换的节点就好了
相信到这里,你看Java线程生命周期的眼神就没那么迷惑了,重点就是RUNNABLE与休眠状态的切换,接下来我们看一看,如何查看线程中的状态,以及具体的代码触发点
如何查看线程处在什么状态
程序中调用 getState()
方法
Thread 类中同样存在 getState()
方法用于查看当前线程状态,该方法就是返回上面提到的枚举类 State
NEW
就是上面提到, 编程语言中特有的,通过继承 Thread 或实现 Runnable 接口定义线程后,这时的状态都是 NEW
Thread thread = new Thread(() -> {});
System.out.println(thread.getState());
RUNNABLE
调用了 start()
方法之后,线程就处在 RUNNABLE 状态了
Thread thread = new Thread(() -> {});
thread.start();
//Thread.sleep(1000);
System.out.println(thread.getState());
BLOCKED
等待 synchronized 内置锁,就会处在 BLOCKED 状态
publicclass ThreadStateTest {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(new DemoThreadB());Thread t2 = new Thread(new DemoThreadB());t1.start();t2.start();Thread.sleep(1000);System.out.println((t2.getState()));System.exit(0);}
}class DemoThreadB implements Runnable {@Overridepublic void run() {commonResource();}public static synchronized void commonResource() {while(true) {}}
}
WAITING
调用线程的 join()
等方法,从 RUNNABLE 变为 WAITING 状态
public static void main(String[] args) throws InterruptedException {Thread main = Thread.currentThread();Thread thread2 = new Thread(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();e.printStackTrace();}System.out.println(main.getState());});thread2.start();thread2.join();}
TIMED-WAITING
调用了 sleep(long)
等方法,线程从 RUNNABLE 变为 TIMED-WAITING 状态
public static void main(String[] args) throws InterruptedException {Thread thread3 = new Thread(() -> {try {Thread.sleep(3000);} catch (InterruptedException e) {// 为什么要调用interrupt方法?Thread.currentThread().interrupt();e.printStackTrace();}});thread3.start();Thread.sleep(1000);System.out.println(thread3.getState());}
TERMINATED
线程执行完自然就到了 TERMINATED 状态了
Thread thread = new Thread(() -> {});
thread.start();
Thread.sleep(1000);
System.out.println(thread.getState());
以上是程序中查看线程,自己写写测试看看状态还好,现实中的程序怎么可能允许你加这么多无用代码,所以,翠花,上酸菜(jstack)
jstack 命令查看
相信你听说过这玩意,jstack 命令就比较强大了,不仅能查看线程当前状态,还能看调用栈,锁等线程栈信息
大家可以随意写一些程序,这里我用了上面 WAITING 状态的代码, 修改睡眠时间 Thread.sleep(100000),然后在终端按照下图标示依次执行下图命令
更多功能还请大家自行查看,后续会单独写文章来教大家如何使用jstack查看线程栈信息
Arthas
这个利器,无须多言吧,线上找茬监控没毛病,希望你可以灵活使用这个工具,攻克疑难杂症
查看线程栈详细信息,非常方便:https://alibaba.github.io/arthas/thread.html
相信你已经和Arthas确认了眼神
关于线程生命周期状态整体就算说完了,编写并发程序时多问一问自己:
调用某个API会将你的线程置为甚么状态?
多问自己几次,自然就记住上面的图了
灵魂追问
为什么调用 Thread.sleep, catch异常后,调用了Thread.currentThread().interrupt();
进入 BLOCKED只有一种情况,就是等待 synchronized 监视器锁,那调用 JUC 中的 Lock.lock() 方法,如果某个线程等待这个锁,这个线程状态是什么呢?为什么?
publicclass ThreadStateTest {public static void main(String[] args) throws InterruptedException {TestLock testLock = new TestLock();Thread thread2 = new Thread(() -> {testLock.myTestLock();}, "thread2");Thread thread1 = new Thread(() -> {testLock.myTestLock();}, "thread1");thread1.start();Thread.sleep(1000);thread2.start();Thread.sleep(1000);System.out.println("****" + (thread2.getState()));Thread.sleep(20000);}
}@Slf4j
class TestLock{privatefinal Lock lock = new ReentrantLock();public void myTestLock(){lock.lock();try{Thread.sleep(10000);log.info("testLock status");} catch (InterruptedException e) {log.error(e.getMessage());} finally {lock.unlock();}}
}
synchronized 和 Lock 有什么区别?
参考
感谢前辈们总结的精华,自己所写的并发系列好多都参考了以下资料
Java 并发编程实战
Java 并发编程之美
码出高效
Java 并发编程的艺术
......
Elasticsearch 在互联网公司大量真实的应用案例
6个接私活的网站,你有技术就有钱!
知乎千万级高性能长连接网关是如何搭建的
别乱提交代码了,看下大厂 Git 提交规范是怎么做的!
start.aliyun.com 正式上线!
推荐关注本文作者
日拱一兵
只产轻松易懂的技术知识讲解
这么说线程生命周期是不是简单了点?相关推荐
- 线程的生命周期_[线程设计]通过观察者模式监控线程生命周期
推荐书籍:<Java高并发编程详解> 一.无法掌控 Thread 的生命周期 在 Java 中线程的实现只有一个,就是 Thread . 下面是启动一个线程的通用代码,如下: 通过 new ...
- java线程基础巩固---线程生命周期以及start方法源码剖析
上篇中介绍了如何启动一个线程,通过调用start()方法才能创建并使用新线程,并且这个start()是非阻塞的,调用之后立马就返回的,实际上它是线程生命周期环节中的一种,所以这里阐述一下线程的一个完整 ...
- java不同进程的相互唤醒_Java线程生命周期与状态切换
前提 最近有点懒散,没什么比较有深度的产出.刚好想重新研读一下JUC线程池的源码实现,在此之前先深入了解一下Java中的线程实现,包括线程的生命周期.状态切换以及线程的上下文切换等等.编写本文的时候, ...
- 线程基础知识_线程生命周期_从JVM内存结构看多线程下的共享资源
线程生命周期 线程状态 New: 线程创建(new Thread()) Runnable: 线程可运行(thread.start()), 注: 调用start并不一定是运行状态, 可能在等待CPU调度 ...
- 详解Java线程生命周期与状态切换
前提 最近有点懒散,没什么比较有深度的产出.刚好想重新研读一下JUC线程池的源码实现,在此之前先深入了解一下Java中的线程实现,包括线程的生命周期.状态切换以及线程的上下文切换等等.编写本文的时候, ...
- iOS线程生命周期的监控
iOS系统通过Core Services层的Foundation框架提供基于OC语言的NSThread和NSOperationQueue类来实现对线程和线程池的管理和使用.同时也提供了一套基于C语言的 ...
- Java线程生命周期与状态切换
前提# 最近有点懒散,没什么比较有深度的产出.刚好想重新研读一下JUC线程池的源码实现,在此之前先深入了解一下Java中的线程实现,包括线程的生命周期.状态切换以及线程的上下文切换等等.编写本文的时候 ...
- iOS多线程全套:线程生命周期,多线程的四种解决方案,线程安全问题,GCD的使用,NSOperation的使用(上)
2017-07-08 remember17 Cocoa开发者社区 目的 本文主要是分享iOS多线程的相关内容,为了更系统的讲解,将分为以下7个方面来展开描述. 多线程的基本概念 线程的状态与生命周期 ...
- 13.7 线程生命周期状态图、线程常用的方法。
package cn.chen.samplethread; import java.lang.*; /* 线程生命周期状态图.线程常用的方法.线程的生命周期:创建状态.等待就绪态.运行状态.阻塞状态. ...
最新文章
- cass地籍参数设置快捷命令_南方cass详解+视频教程+插件汇总,小白快速上手!限时领取...
- python3官方最新下载-Python3.9下载
- Android图片资源获取原则
- JAVA程序禁用Hbase中的表_HBase禁用表
- python颜色库_Python可视化_matplotlib08-palettable颜色库(四)
- 同时支持EF+Dapper的混合仓储,助你快速搭建数据访问层
- 简单的C语言开源软件,自己写的一个简单的数据库...完整开源...
- Iview的Tabs设置居中
- html静态页面实现微信分享思路
- 在html5中插入flash,如何将Flash嵌入到HTML5中?
- 10分钟搞懂商业模式画布:9张分析图、6张模板
- 2021-2027全球与中国铂金芯片温度传感器市场现状及未来发展趋势
- Word-去掉标题前面的小黑点
- 23种设计模式——最好理解的代理模式
- 简易购买电影票系统(Java)
- 生成彩色二维码(渐变色、插入图片和文字)
- 第五章:3ds max UV展开和BP贴图绘制(下)
- (最详细教程)中国知网caj格式论文如何转化为word,pdf,txt
- 雷霆行动(STG飞机游戏)源码分析
- Java解析快手视频去水印教程--2020年10月最新有效代码