按照官方的说明java 的thread 有以下几种状态:

  • NEW
  • RUNNABLE
  • BLOCKED
  • WAITING
  • TIMED_WAITING
  • TERMINATED

会发现通过jstack 打印出来的线程状态不是这样的。

下面这个图是通过IBM 的jca 工具来分析jstack dump文件。顺便说一下jca 是目前发现最好的研究线程栈的工具,本地工具秒杀所有在线分析网站。可以从 IBM Thread and Monitor Dump Analyzer for Java (TMDA) 下载。但是有一个地方他显示的状态有略微不同,这个下面会讲到。

这里按照我的理解是java 里的状态是针对java 编码系统的,而通过jstack 打印出来的状态是基于JVM底层的状态来显示的。接下来我们来分别模拟各种状态可能对应的代码,来研究对应真实场景中我们的线程在jvm中的状态。

首先我们需要一段代码来模拟程序的运行时间,这里不能直接使用sleep,因为sleep 本身状态会对线程状态产生干扰。

 /*** 模拟cpu运行** @param duration*/public static void cpuRun(long duration) {final long startTime = System.currentTimeMillis();int num = 0;while (true) {num++;if (num == Integer.MAX_VALUE) {System.out.println(Thread.currentThread() + "rest");num = 0;}if (System.currentTimeMillis() - startTime > duration) {return;}}}

Runnable

该状态表示线程具备所有运行条件,在运行队列中准备操作系统的调度,或者正在运行。

New和普通的Runnable太简单了,没啥好讲的

public static void NEW() {Thread t = new Thread();System.out.println(t.getState());}public static void RUNNABLE() throws InterruptedException {Thread t = new Thread() {@Overridepublic void run() {cpuRun(20000);}};t.start();System.out.println(t.getState());t.join();}

我们研究下,当线程等待io的时候是什么状态

/*** 在io 阻塞读的时候线程状态也是runnable的。** @throws InterruptedException*/public static void runnableInBlockedIO() throws InterruptedException {Scanner in = new Scanner(System.in);Thread t1 = new Thread("demo-t1") {@Overridepublic void run() {try {System.out.println("start io read---------");// 命令行中的阻塞读String input = in.nextLine();System.out.println(input);} catch (Exception e) {e.printStackTrace();} finally {IOUtils.closeQuietly(in);}}};t1.start();t1.join();}

下图是打印的线程栈

可以看到是Runnable ,同样等待socket 连接的时候也一样。

public static void runnableInBlockedSocket() throws InterruptedException {Thread serverThread = new Thread(new Runnable() {@Overridepublic void run() {ServerSocket serverSocket = null;try {serverSocket = new ServerSocket(10086);while (true) {System.out.println("start socket accept");// 阻塞的accept方法Socket socket = serverSocket.accept();System.out.println("end socket accept");}} catch (IOException e) {e.printStackTrace();} finally {try {serverSocket.close();} catch (IOException e) {e.printStackTrace();}}}}, "demo-in-socket"); // 线程的名字serverThread.start();serverThread.join();}

这里没有尝试NIO相关的状态,后面研究netty的时候可以进一步研究下。

waiting for monitor entry

这个状态通常是线程争抢锁,被block住了。

public static void BLOCKED() {final Object lock = new Object();Runnable run = new Runnable() {@Overridepublic void run() {for (int i = 0; i < Integer.MAX_VALUE; i++) {synchronized (lock) {cpuRun(500);System.out.println(i);}}}};Thread t1 = new Thread(run);t1.setName("t1");Thread t2 = new Thread(run);t2.setName("t2");t1.start();t2.start();}

从线程栈可以看出t1拿到了锁,所以是Runnable,t2没拿到锁所以被Block。

这里需要注意一下,jstack 里显示的是“waiting for monitor entry”,而jca 显示的是“waiting on monitor”,这个跟另一个状态“waiting on condition” 非常像,之前就因为这个问题误判过,非常坑爹。

WAITING

代码中直接调用wait方法。

 public static void WAITING() {final Object lock = new Object();Thread t1 = new Thread() {@Overridepublic void run() {int i = 0;while (true) {synchronized (lock) {System.out.println("t1 running");cpuRun(2000);try {lock.wait();} catch (InterruptedException e) {}System.out.println(i++);System.out.println("t1 end");}}}};Thread t2 = new Thread() {@Overridepublic void run() {while (true) {synchronized (lock) {System.out.println("t2 running");cpuRun(5000);lock.notifyAll();System.out.println("t2 end");}//这里需要一定时间执行,否则t1 可能一直抢不到锁cpuRun(100);}}};t1.setName("^^t1^^");t2.setName("^^t2^^");t1.start();t2.start();}

这里jstack 原文显示的是“WAITING (on object monitor)”,jca中显示“Object.wait()” 略有区别。

waiting on condition

这个状态非常复杂,也是这次动手分析的主要原因。先看下网上的一些官方说法:

该状态出现在线程等待某个条件的发生。具体是什么原因,可以结合 stacktrace来分析。最常见的情况是线程在等待网络的读写,比如当网络数据没有准备好读时,线程处于这种等待状态,而一旦有数据准备好读之后,线程会重新激活,读取并处理数据。如果发现有大量的线程都在处在 Wait on condition,从线程 stack看,正等待网络读写,这可能是一个网络瓶颈的征兆。因为网络阻塞导致线程无法执行。一种情况是网络非常忙,几乎消耗了所有的带宽,仍然有大量数据等待网络读写;另一种情况也可能是网络空闲,但由于路由等问题,导致包无法正常的到达。所以要结合系统的一些性能观察工具来综合分析,比如 netstat统计单位时间的发送包的数目,如果很明显超过了所在网络带宽的限制 ; 观察 cpu的利用率,如果系统态的 CPU时间,相对于用户态的 CPU时间比例较高;如果程序运行在 Solaris 10平台上,可以用 dtrace工具看系统调用的情况,如果观察到 read/write的系统调用的次数或者运行时间遥遥领先;这些都指向由于网络带宽所限导致的网络瓶颈。
另外一种出现 Wait on condition的常见情况是该线程在 sleep,等待 sleep的时间到了时候,将被唤醒。

一句话具体问题,具体分析。接下来会根据经验尽量模拟遇到的各种场景,但也不一定完全覆盖。

比较简单的sleep

public static void SLEEP() {Thread t1 = new Thread("t1") {@Overridepublic void run() {try {Thread.sleep(200000);} catch (InterruptedException e) {e.printStackTrace();}}};t1.start();}

线程池空闲的时候

public static void threadPoolFree() throws InterruptedException {System.out.println("all start");ThreadPoolExecutor threadPoolExecutor = buildThreadPool();Thread.sleep(3000);System.out.println("warmup end,start 1 runnable");threadPoolExecutor.execute(new Runnable() {@Overridepublic void run() {cpuRun(500000);System.out.println("last thread over");}});System.out.println("all end");}

这里构造线程池的时候,进行了预热,确保线程池被充分活跃过。

public static ThreadPoolExecutor buildThreadPool() {ThreadFactory threadFactory = (new ThreadFactoryBuilder()).setNameFormat("demo-test-%d").build();ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 5, 0, TimeUnit.SECONDS,new LinkedBlockingQueue(1000), threadFactory);//这里快速预热一下,让线程池充分初始化for (int i = 0; i < 20; i++) {threadPoolExecutor.execute(new Runnable() {@Overridepublic void run() {cpuRun(10);}});}return threadPoolExecutor;}

可以看到线程池里5个线程,只有1个是Runnable,其他4个都是Waiting on condition。注意这里具体对账跟上面sleep的区别。核心是在ThreadPoolExecutor.getTask这行上,通过研究过ThreadPoolExecutor的代码可以知道,是在捞队列里面的任务,这个时候park说明队列里面没有任务。很多时候我们线上排查问题遇到比较多的是这个状态,这里可以试一下让线程池繁忙起来的时候,线程池里线程的状态就全是Runnable,所以监控当前线程池的活跃线程数非常重要,它反映了我们系统压力的健康度。这里需要注意threadPoolExecutor.getPoolSize()和threadPoolExecutor.getActiveCount() 的区别,前者是线程池大小,后者是活跃线程数。

这些都是本地模拟的比较简单的情况,实际线上的情况会复杂很多,最终都跟线程池的机制密不可分,线程池的源码分析等有机会再整理下,同时上面没有分析NIO的情况,未完待续...

java线程状态研究相关推荐

  1. Java 线程状态之 TIMED_WAITING

    定义 一个正在限时等待另一个线程执行一个动作的线程处于这一状态. A thread that is waiting for another thread to perform an action fo ...

  2. 面试必备,Java线程状态之细节回顾

    点击上方"方志朋",选择"设为星标" 做积极的人,而不是积极废人 来源:https://dwz.cn/vYqjShos Java线程有6种状态 在某个给定时间点 ...

  3. Java线程状态及 wait、sleep、join、interrupt、yield等的区别

    Java中的线程状态(详见Java线程状态及转换-MarchOn): wait:Object类的实例方法,释放CPU执行权,进入等待状态,直到  被中断.被拥有该对象锁的线程唤醒(notify或not ...

  4. java runnable wait_面试官:都说阻塞 I/O 模型将会使线程休眠,为什么 Java 线程状态却是 RUNNABLE?...

    摘要: 原创出处 https://studyidea.cn 「公众号:程序通事 」欢迎关注和转载,保留摘要,谢谢! 使用 Java 阻塞 I/O 模型读取数据,将会导致线程阻塞,线程将会进入休眠,从而 ...

  5. java 线程状态_Java线程为何没有Running状态?我猜你不知道。

    作者:国栋原文:https://my.oschina.net/goldenshaw/blog/705397 Java虚拟机层面所暴露给我们的状态,与操作系统底层的线程状态是两个不同层面的事.具体而言, ...

  6. java线程切换 notify_浅谈 Java线程状态转换及控制

    作者:城北有个混子 出自:博客园 1.线程的状态(系统层面) 一个线程被创建后就进入了线程的生命周期.在线程的生命周期中,共包括新建(New).就绪(Runnable).运行(Running).阻塞( ...

  7. Java线程状态Jstack线程状态BLOCKED/TIMED_WAITING/WAITING解释

    一.线程5种状态 新建状态(New) 新创建了一个线程对象. 就绪状态(Runnable) 线程对象创建后,其他线程调用了该对象的start()方法.该状态的线程位于可运行线程池中,变得可运行,等待获 ...

  8. java 线程状态_面试官问:为什么Java线程没有Running状态?我懵了

    点击上方"占小狼的博客",选择"设为星标" 本文阅读时间大约4分钟. 来源:https://dwz.cn/dLRLBZab Java虚拟机层面所暴露给我们的状态 ...

  9. 【图解】透彻Java线程状态转换

    大家好,我是阿星,好久不见,欢迎来到Java并发编程系列番外篇线程状态转换,内容通俗易懂,请放心食用. 线程状态 先来个开场四连问 Java线程状态有几个? Java线程状态是如何转换? Java线程 ...

最新文章

  1. Eclipse下svn的创建分支/合并/切换使用
  2. 对抗攻击层出不穷?神经科学带来新突破、导出智能统一框架,Hinton:我早有洞见...
  3. mysql三范式和反三范式_数据库三范式和反三范式
  4. hive -e和hive -f的区别(转)
  5. 代码注释掉还能执行_日志消息是可执行代码和注释
  6. Java+Jmeter接口测试
  7. 怎么将电脑的图片不用打开也能显示出来
  8. SpringBoot 工程目录 整合mybatis-mysql(注解类型)
  9. linux chown命令_Linux chown命令示例
  10. POJ2752 (Seek the Name, Seek the Fame,kmp)
  11. 【读书分享】《解忧杂货店》东野圭吾
  12. java计算机毕业设计民航售票管理系统源码+系统+数据库+lw文档+mybatis+运行部署
  13. java进阶--深入理解Java自动装箱拆箱机制(Autoboxing and unboxing)
  14. HDU 4043 FXTZ II
  15. 蓝桥杯算法考前复习要点和归纳总结
  16. 聚焦大规模分布式机器学习,全面剖析Google TensorFlow,来看阿里、京东等在人工智能技术的进展...
  17. Python生成带圆角图片的二维码
  18. spinner添加分割线和改变右侧箭头
  19. php 画布旋转,gdip图形库,画布如何进行按照指定的角度进行旋转
  20. 库卡工业机器人负载曲线图_KUKA/库卡机械臂 KR500 R2830工业机器人负载500KG

热门文章

  1. 【浙大pta大计基】
  2. 用sqlcipher对已有的SQLite数据库加密
  3. 查看linux文件生成时间,【linux】如何查看文件的创建、修改时间
  4. Docker+VSCode 配置属于自己的深度学习炼丹炉
  5. matplotlib显示伪彩色图像及色度条
  6. 详解BlockingQueue
  7. Aurora 8B/10B光口通信
  8. 学习OpenCV3:Cmake+MinGW编译OpenCV
  9. ntoskrnl.exe损坏或丢失的解决方案
  10. apache中的php模块安装