• 通用线程模型

在很多研发当中,实际应用是基于一个理论再进行优化的。所以,在了解JVM规范中的Java线程的生命周期之前,我们可以先了解通用的线程生命周期,这有助于我们后续对JVM线程生命周期的理解。

首先,通用的线程生命周期有五种,分别是:新建状态(NEW)、可运行状态(RUNNABLE)、运行状态(RUN)、休眠状态(SLEEP)、终止状态(TERMINATED)。生命流程如下图所示:

  1. 新建状态(NEW)。线程在此状态,仅仅是在编程语言层面创建了此线程,而在真正的操作系统中是没有创建的。所以,它在这个状态下是无法获得CPU的执行的权限的。
  2. 可运行状态(RUNNABLE)。线程到达此状态,意味着它已经被操作系统创建,该线程获得被CPU执行的资格,但此时还没有被CPU执行相关操作。
  3. 运行状态(RUN)。线程获得CPU的执行权限,在一个特定的时间片内执行,线程仅在这个时间片内被称为运行状态。
  4. 休眠状态(SLEEP)。当线程调用了某个阻塞API或者等待IO操作的时候,它会释放当前CPU的执行权限,进入休眠状态。此时,线程没有获取CPU执行的资格,只有当该线程被唤醒时,线程才能进入RUNNABLE状态。
  5. 终止状态(TERMINATED)。当线程完成程序任务或者出现异常的时候,它就会进入终止状态。一个线程的使命就此结束。
  • JVM线程模型

JVM中的线程模型对于上面的通用线程模型进行了一些特有的分类和合并,它们的类别如下:

  1. 新建状态(NEW)
  2. 可运行/运行状态(RUNNABLE)
  3. 阻塞状态(BLOCK)
  4. 等待状态(WAITING)
  5. 有限等待状态(TIMED_WATING)
  6. 终止状态(TERMINATED)

而JVM中的状态结合到通用状态中可以如下图所示理解:

由上图可以看出,JVM讲运行中的线程和等待运行的线程归为一类,因为JVM不关心操作系统层面的调度,所以把这两个状态合并了。而Block、Wating、Timed_Wating三个状态在操作系统层面都为休眠状态没有区别。所以,这三种状态都没有获得CPU执行的资格

  • 线程状态的转换

从上面的JVM线程生命周期图分析,我来说说一个线程从新建到消亡的状态转变中,到底会发生什么事情。

  1. 从NEW到RUNNABLE

这个很简单,线程在被显式声明后,在调用start()方法前,这段时间都被称为NEW状态。如下代码所示

   Runnable task = ()-> System.out.println("线程启动");
//创建一个线程,线程状态为NEW状态
Thread thread = new Thread(task);

  2.从RUNNABLE到BLOCK

从RUNNABLE到BLOCK状态的转变只有一种途径,那就是在有synchronized关键字的程序当中。当线程执行到此,没有获取到synchronized的隐式锁,线程就会从RUNNABLE被阻塞为BLOCK状态。当阻塞中的线程获取到synchronized隐式锁时,它又会转变为RUNNABLE状态。

问:当线程调用阻塞API时,它的状态会不会改变呢? 例如我们日常说的:ServerSockt的accept()、Scanner的next()方法等。

答案是:对于JVM层面来说,调用这些方法的线程依旧在RUNNABLE状态,因为JVM对于等待CPU资源或等待IO资源并不关心,所以把他们归为RUNNABLE状态。而对于操作系统层面来说,线程则属于休眠状态。(对于较真的同学,可以通过jstack指令查看调用阻塞API是的线程是什么状态

  3.RUNNABLE到WATING

其中有三种场景会使线程转换为WATING状态:

  1. synchronized的内部,调用wait()方法。
  2. 调用Thread.join()方法。该方法的意思是,当一个A线程调用了B线程的join方法,那么A线程就必须等待B线程执行完毕,此时,A线程就为WATING状态。需要注意的是,如果是B线程自己调用自己的join方法。那么就会造成自己等待自己的局面,从而使线程无限等待。
  3. 调用LockSupport.park()方法。这个方法看上去很陌生,但是其实jdk中的并发包中的锁都是由它实现的。例如:我们日常中用到的lock.lock()方法,condition.await()方法,其底层都是通过调用这个方法运行的。

  4.RUNNABLE到TIMED_WATING状态

其实从字面上就可以看出,TIMED_WATING状态与WATING状态的差别就是TIMED_WATING会在有限的时间内等待。所以,在WATING方法中的大多数方法,只要加上一个时间参数,就会触发TIMED_WATING这个状态。具体的有:

         Thread.currentThread().join(millis);
Thread.sleep(millis);
Obj.wait(timeout);
LockSupport.parkNanos(Object blocker, long deadline);
LockSupport.parkUntil(long deadline);

  5.RUNNABLE到TERMINAL状态

当线程顺利的完成run()方法中的任务,就会进入TERMINAL状态。同时,当线程抛出没有处理异常的时候,线程同样会变为TERMINAL状态。那如果业务上需要我们主动的终止线程,那应该怎么做呢?

  • 终止线程的正确姿势

在以往的jdk中,它提供了一些诸如:stop()、suspend()、resume()方法,这些方法都会直接把线程关闭,不给线程任何处理的机会。这样做的风险可想而知,所以这些方法早就已经被标记为过时方法,不推荐使用,我也没有详细去了解。那么,我们现在想要终止一个线程,该怎么做呢?

答案就是:通过调用thread.interrupt();方法来达到终止线程的目的。当然,并不是调用interrupt()就会关闭线程,我们通过一个图来了解一下具体的流程是怎样的。

如图所示,线程状态的不同,对于Interrupt方法的处理也不同。流程在图中已经比较清晰,我再列出几个重点:

  1. Interrupt方法仅仅是把线程是否被中断的标识设置为true
  2. 当抛出InterruptedException时会把中断标志清除
  3. 被中断的线程状态不同,做出的响应也会不同。运行时线程需要主动检测、等待时的异常会抛出异常(这里可以类比硬件中的中断,相当于一个信号)。
  • 总结

我们在日常开发中,一旦遇到多线程的bug,分析线程dump信息是一个非常重要的手段。而了解线程运行时的状态,有助于在分析信息时正确的判断线程的状况。同样,我们可以通过jstack命令或者Java VisualVM可视化工具来查看线程的具体信息。

并发编程 || Java线程详解的更多相关文章

  1. 从缓存入门到并发编程三要素详解 Java中 volatile 、final 等关键字解析案例

    引入高速缓存概念 在计算机在执行程序时,以指令为单位来执行,每条指令都是在CPU中执行的,而执行指令过程中,势必涉及到数据的读取和写入. 由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,这 ...

  2. Android并发编程之白话文详解Future,FutureTask和Callable

    从最简单的说起Thread和Runnable 说到并发编程,就一定是多个线程并发执行任务.那么并发编程的基础是什么呢?没错那就是Thread了.一个Thread可以执行一个Runnable类型的对象. ...

  3. 并发编程——IO模型详解

    ​ 我是一个Python技术小白,对于我而言,多任务处理一般就借助于多进程以及多线程的方式,在多任务处理中如果涉及到IO操作,则会接触到同步.异步.阻塞.非阻塞等相关概念,当然也是并发编程的基础. ​ ...

  4. java线程详解

    Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ...

  5. Java线程详解----借鉴

    Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ...

  6. 【转】Java线程详解

    Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ...

  7. 并发编程——Java线程的6种状态及切换

    前言 本次主要分享一下Java线程的六种状态及其转换. 如果对于线程的创建方式不太了解,推荐观看并发编程--认识java里的线程 线程的状态及其转换 操作系统线程的五种状态 新建(NEW) 就绪(RU ...

  8. java线程详解(三)

    java线程间通信 首先看一段代码 class Res { String name; String sex; } class Input implements Runnable { private R ...

  9. java线程——详解Callable、Future和FutureTask

    回顾: 接上篇博客 java线程--三种创建线程的方式,这篇博客主要介绍第三种方式Callable和Future.比较继承Thread类和实现Runnable接口,接口更加灵活,使用更广泛.但这两种方 ...

随机推荐

  1. 学习-angular 7入门

    1.安装脚手架:npm install -g @angular/cli 安装之后,输入命令 ng v: Package Version -------------------------------- ...

  2. python的小数据池

    一.什么是小数据池? 小数据池是一种缓存机制,也被称为驻留机制.各种编程语言中都有类似的东西(常量池.小数据池都是指得同一个内容). python自动将-5~256的整数.有一定规则的字符串.都放在一 ...

  3. impala进阶

    一.impala存储 1.文件类型 2.压缩方式 二.impala分区 1.创建分区方式 partitioned by 创建表时,添加该字段指定分区列表: create table t_person( ...

  4. Pytorch循环神经网络LSTM时间序列预测风速

    #时间序列预测分析就是利用过去一段时间内某事件时间的特征来预测未来一段时间内该事件的特征.这是一类相对比较复杂的预测建模问题,和回归分析模型的预测不同,时间序列模型是依赖于事件发生的先后顺序的,同样大 ...

  5. oracle 错误 ORA-00020问题解析

    问题描述 [oracle@xiaowu ~]$ sqlplus / as sysdba SQL*Plus: Release Production on Wed Oct :: Copyright (c) ...

  6. Zookeeper 运维实践手册

    Zookeeper是一个高可用的分布式数据管理与协调框架,该框架能很好地保证分布式环境中数据一致性.一般用来实现服务发现(类似DNS),配置管理,分布式锁,leader选举等. 一.生产环境中Zook ...

  7. layui排序功能

    后台常用功能之排序!!! 一次只能排一个序!!! 基本样式 <tr> <th>序号<span class="layui-table-sort layui-inl ...

  8. Python的设计哲学--zen of Python

               Python的设计哲学--zen of Python Beautiful is better than ugly. 优美胜于丑陋 Explicit is better than ...

  9. 简要介绍Active Learning(主动学习)思想框架,以及从IF(isolation forest)衍生出来的算法:FBIF(Feedback-Guided Anomaly Discovery)

    1. 引言 本文所讨论的内容为笔者对外文文献的翻译,并加入了笔者自己的理解和总结,文中涉及到的原始外文论文和相关学习链接我会放在reference里,另外,推荐读者朋友购买 Stephen Boyd的 ...

  10. Lock+Condition实现机制

    前言:大部分多线程同步场景,在功能和性能层面,synchronized可以满足,少部分场景Lock可以满足,dubbo的源码也符合这个比例,需要使用到Condition的场景极少,整个dubbo源码中 ...