文章简介

这一篇主要围绕线程状态控制相关的操作分析线程的原理,比如线程的中断,线程的通信等,内容比较多,可能会分两篇文章

内容导航

  • 线程的启动的实现原理
  • 线程停止的实现原理分析
  • 为什么中断线程会抛出InterruptedException的

一、线程的启动原理

前面我们简单分析过了线程的使用,通过调用线程的启动方法来启动线程,线程启动后会调用运行方法执行业务逻辑,运行方法执行完毕后,线程的生命周期也就终止了。

很多同学最早学习线程的时候会比较疑惑,启动一个线程为什么是调用启动方法,而不是运行方法,这做一个简单的分析,先简单看一下启动方法的定义

我们看到调用启动方法实际上是调用一个本地方法START0()来启动一个线程,首先START0()这个方法是在线程的静态块中来注册的,代码如下

这个registerNatives的作用是注册一些本地方法提供给线程类来使用,比如START0(),的IsAlive(),currentThread(),睡眠();这些都是大家很熟悉的方法。

registerNatives的本地方法的定义在文件Thread.cThread.c定义了各个操作系统平台要用的关于线程的公共数据和操作,以下是Thread.c的全部内容

从这段代码可以看出,start0(),实际会执行JVM_StartThread方法,这个方法是干嘛的呢?从名字上来看,似乎是在JVM层面去启动一个线程,如果真的是这样,那么在JVM层面,一定会调用的Java中定义的运行方法那接下来继续去找找答案我们找到。这个文件,这个文件需要下载热点的源码才能找到。jvm.cpp

JVM_ENTRY用来的英文定义JVM_StartThread函数的,在这个函数里面创建了一个真正和平台有关的本地线程。本着打破砂锅查到底的原则,看看继续做了什么事情,继续寻找JavaThread的定义newJavaThread

在热点源码的中文件中1558行的位置可以找到如下代码thread.cpp

这个方法有两个参数,第一个是函数名称,线程创建成功之后会根据这个函数名称调用对应的函数;第二个是当前进程内已经有的线程数量最后我们重点关注与一下,实际就是调用平台创建线程的方法来创建线程。os::create_thread

接下来就是线程的启动,会调用Thread.cpp文件中的Thread :: start(Thread * thread)方法,代码如下

启动方法中有一个函数调用:,调用平台启动线程的方法,最终会调用Thread.cpp文件中的JavaThread :: run()方法os::start_thread(thread);

这个方法中主要是做一系列的初始化操作,最后有一个方法thread_main_inner,接下来看看这个方法的逻辑是什么样的

和主流程无关的代码咱们先不去看,直接找到最核心的代码块,这个入口点应该比较熟悉了,因为我们在前面提到了,在:: JavaThread这个方法中传递的第一个参数,代表函数名称,线程启动的时候会调用这个函数。this->entry_point()(this,this);

如果大家还没有晕车的话,应该记得我们在jvm.cpp文件中看到的代码,在创建的时候传递了一个线程native_thread=newJavaThread(&thread_entry,sz);入口函数,所以我们在jvm.cpp中找到这个函数的定义如下

看到可以这个调用,其实就是通过回调方法调用Java的线程中定义的运行方法,是一个宏定义,在vmSymbols.hpp文件中可以找到如下代码vmSymbols::run_method_name()run_method_name

所以结论就是,Java的里面创建线程之后必须要调用启动方法才能真正的创建一个线程,该方法会调用虚拟机启动一个本地线程,本地线程的创建会调用当前系统创建线程的方法进行创建,并且线程被的执行会时候回调run方法进行业务逻辑的处理

二、线程的终止方法及原理

线程的终止有主动和被动之分,被动表示线程出现异常退出或者运行方法执行完毕,线程会自动终止。主动的方式的英文来实现线程的终止,但是停止()方法是一个过期的方法,官方是不建议使用,理由很简单,stop()方法在中介一个线程时不会保证线程的资源正常释放,也就是不会给线程完成资源释放工作的机会,相当于我们在linux上通过kill -9强制结束一个进程。Thread.stop()

那么如何安全的终止一个线程呢?

我们先看一下下面的代码,代码演示了一个正确终止线程的方法,至于它的实现原理,稍后我们再分析

代码中有两处需要注意,在主线程中,调用了线程的中断()方法,在运行方法中,而中循环通过来判断线程中断的标识。所以我们在这里猜想一下,应该是在线程中维护了一个中断标识,通过方法去改变了中断标识的值使得方法中,而循环的判断不成立而跳出循环,因此运行方法执行完毕以后线程就终止了运行。Thread.currentThread().isInterrupted()thread.interrupt()

线程中断的原理分析

来看我们一下方法做了什么事情thread.interrupt()

这个方法里面,调用了中断0(),这个方法在前面分析启动方法的时候见过,是一个本机方法,这里就不再重复贴代码了,同样,我们找到jvm.cpp文件,找到JVM_Interrupt的定义

这个方法比较简单,调用直接了这个方法,这个方法的定义在Thread.cpp文件中,代码如下Thread::interrupt(thr)

主题::中断方法调用了OS ::中断方法,这个是调用平台的中断方法,方法这个实现的的英文在文件中,其中星号代表的是不同平台,因为JVM是跨平台的,所以对于不同的操作平台,线程的调度方式都是不一样的。我们以os_linux.cpp文件为例os_*.cpp

通过上面的代码分析可以知道,了Thread.interrupt()方法实际就是设置一个中断状态标识为真,并且通过ParkEvent的取消驻留方法来唤醒线程。

  • 对于同步阻塞的线程,被唤醒以后会继续尝试获取锁,如果失败仍然可能被公园
  • 在调用ParkEvent的公园方法之前,会先判断线程的中断状态,如果为真,会清除当前线程的中断标识
  • 的Object.wait,了Thread.sleep,的Thread.join会抛出InterruptedException的

这里给大家普及一个知识点,为什么的Object.wait,的Thread.sleep和的Thread.join都会抛出InterruptedException的?首先,这个异常的意思是表示一个阻塞被其他线程中断了。然后,由于线程调用了中断()中断方法,那么的Object.wait,的Thread.sleep等被阻塞的线程被唤醒以后会通过is_interrupted方法判断中断标识的状态变化,如果发现中断标识为真,则先清除中断标识,然后抛出InterruptedException的

需要注意的是,InterruptedException的异常的抛出并不意味着线程必须终止,而是提醒当前线程有中断的操作发生,至于接下来怎么处理取决于线程本身,比如

  • 直接捕获异常不做任何处理
  • 将异常往外抛出
  • 停止当前线程,并打印异常信息

为了让大家能够更好的理解上面这段话,我们以了Thread.sleep为例直接从JDK的源码中找到中断标识的清除以及异常抛出的方法代码

找到方法,LINUX平台中的实现在os_linux.cpp文件中,代码如下is_interrupted()

找到了Thread.sleep这个操作在JDK中的源码体现,怎么找?相信如果前面大家有认真看的话,应该能很快找到,代码在jvm.cpp文件中

注意上面加了中文注释的地方的代码,先判断is_interrupted的状态,然后抛出一个InterruptedException的异常。到此为止,我们就已经分析清楚了中断的整个流程。

Java的线程的中断标识判断

了解了了Thread.interrupt方法的作用以后,再回过头来看的Java中这段代码,就很好理解了。由于前者先设置了一个中断标识为真实的,所以这个方法的返回值为真,故而不满足而循环的判断条件导致退出循环。Thread.currentThread().isInterrupted()isInterrupted()

这里有必要再提一句,就是这个线程中断标识有两种方式复位,第一种是前面提到过的InterruptedException的;另一种是通过Thread.interrupted()对当前线程的中断标识进行复位。

粉丝福利,需获取JVM、分布式、微服务、spring等最新相关架构资料

关注我+转发此文+私信回复关键词:架构

tomcat线程循环异常终止_资深架构师带你从JVM层面了解线程的启动和停止相关推荐

  1. tomcat线程循环异常终止_腾讯面试官:如何停止一个正在运行的线程?我一脸蒙蔽。。。...

    1. 停止不了的线程 2. 判断线程是否停止状态 3. 能停止的线程--异常法 4. 在沉睡中停止 5. 能停止的线程---暴力停止 6.方法stop()与java.lang.ThreadDeath异 ...

  2. tomcat 参数最佳实践_一线架构师开发总结:剖析并发编程+JVM性能,深入Tomcat与MySQL...

    每一个程序员都有自己清晰的职业规划和终极目标,无论之后是继续钻研技术,还是转管理岗.产品岗,都是需要自己具备有一定的实力,换句话说技术要牛逼.架构师,是很多程序员的终极目标,而成为一名Java架构师, ...

  3. c# 微服务学习_资深架构师学习笔记:什么是微服务?

    们先来看看为什么要考虑使用微服务. 构建单体应用 我们假设,您开始开发一个打车应用,打算与 Uber 和 Hailo 竞争.经过初步交流和需求收集,您开始手动或者使用类似 Rails.Spring B ...

  4. JAVA资深架构师成长路线-架构师筑基必备技能-深入Tomcat底层

    文章目录 JAVA资深架构师成长路线大纲 深入Tomcat底层 Tomcat体系架构 Tomcat容器及运行机制 Tomcat类加载机制 Tomcat核心组件 Tomcat优化 Tomcat面试题集 ...

  5. python爬虫架构师之路_一位资深 架构师大牛给予Java技术提升的学习路线建议

    一位资深 架构师大牛给予Java技术提升的学习路线建议 对于工作多年的程序员而言,日后的职业发展无非是继续专精技术.转型管理和晋升架构师三种选择. 架构师在一家公司有多重要.优秀架构师需要具备怎样的素 ...

  6. 你和阿里资深架构师之间,差的不仅仅是年龄(进阶必看)

    导读:阅读本文需要有足够的时间,笔者会由浅到深带你一步一步了解一个资深架构师所要掌握的各类知识点,你也可以按照文章中所列的知识体系对比自身,对自己进行查漏补缺,觉得本文对你有帮助的话,可以点赞关注一下 ...

  7. jsp进入另一个jsp分块之后js代码失效_一个架构师的缓存修炼之路

    一位七牛的资深架构师曾经说过这样一句话: " Nginx+业务逻辑层+数据库+缓存层+消息队列,这种模型几乎能适配绝大部分的业务场景. 这么多年过去了,这句话或深或浅地影响了我的技术选择,以 ...

  8. 分享Android资深架构师的成长之路,建议收藏

    背景 经常有网友在知乎问答提两个问题:"现在学习移动开发还有前景吗?""开发还有什么可以研究的?".网友回复:"现在还学移动开发?如同49年加入国军! ...

  9. 你和阿里资深架构师之间,差的不仅仅是年龄(进阶必看) 1

    读:阅读本文需要有足够的时间,笔者会由浅到深带你一步一步了解一个资深架构师所要掌握的各类知识点,你也可以按照文章中所列的知识体系对比自身,对自己进行查漏补缺,觉得本文对你有帮助的话,可以点赞关注一下. ...

最新文章

  1. 树莓派centos安装的基本配置
  2. web 平台搭建-LAMP-源码包(CentOS-6)
  3. 让串口可以自动分辨一帧数据
  4. 2019年需要探究的十大战略性技术趋势
  5. 声学漫谈之六:音腔频段如何判定?
  6. 批处理 正则表达式(findstr) 整理
  7. 北京一公交车发生爆炸 疑为乘客携带药品起反应
  8. .Net微服务实战之DevOps篇
  9. 使用自定义线程池处理并行数据库流
  10. hbase-client1.2.0新版api获取数据方式
  11. IPv6转换服务正式发布
  12. TypeError at /**/ ** missing 1 required positional argument: '**'
  13. 1000道Python题库系列分享19(81道填空判断题)
  14. 大数据分析的四大趋势有哪些
  15. 7-2 前序序列创建二叉树
  16. html 免费设计,十款最佳免费Web设计软件一览
  17. 《Unity 3D人工智能编程》——2.2 子弹类
  18. 超 3000 个特效镜头,复联 4 是如何「速成」的?
  19. 《情书》——岩井俊二
  20. trinity运行原理及常见报错(一)

热门文章

  1. NHibernate利用Mindscape.NHibernateModelDesigner实现数据库与实体之间的转换及操作
  2. linux shell脚本备份mysql数据库
  3. css @media 响应式布局
  4. Create view failed with ORA-01031:insufficient privileges
  5. [转载] Python中的switch语句的替代品
  6. 什么是算术运算和逻辑运算_8086微处理器的算术和逻辑运算
  7. oracle认证都需要考哪几个方面,Oracle OCP认证要通过哪些考试
  8. android开发自动提示框,Android 多种简单的弹出框样式设置代码
  9. 360更新补丁一直提示正在安装_远程利用POC公布|CVE20200796:微软发布SMBv3协议“蠕虫级”漏洞补丁通告...
  10. java删除javaee_JavaEE--集合--删除List中指定元素