哈喽,我是子牙。十余年技术生涯,一路披荆斩棘从技术小白到技术总监到JVM专家到创业。技术栈如汇编、C语言、C 、Windows内核、Linux内核。特别喜欢研究虚拟机底层实现,对JVM有深入研究。分享的文章偏硬核,很硬的那种。
手撸过JVM、内存池、垃圾回收算法、synchronized、线程池、NIO、三色标记算法…
今天咱们来谈谈Java线程创建的一些细节问题:
- Java线程是如何与OS线程建立联系的
- Java线程与OS线程共用一套线程状态吗
- Java线程是如何做到创建与启动分开的
- Java线程在JVM层面为什么要有JavaThread与OSThread
- Java线程为什么设计的时候要将创建与启动分开
- ……
本篇文章的观点都聚焦于Linux平台,不适用于所有平台。因为不同OS,底层差异还是挺大的。
Linux线程创建
线程能力是操作系统才有的,固Java的线程机制一定是基于OS的线程机制实现的,加上些许JVM自身的考虑在其中。这些考虑在哪能看到?JSR规范中。
上一段Linux平台下创建线程的代码
这样创建出来的线程,一般称为原生线程,或native thread。Java的线程实现其实就是将Linux下的线程机制基于JSR规范进行设计重组。如果我们了解Linux的线程机制,并搞明白了JVM是如何进行设计重组的。那么,Java的多线程,我们就算真正学明白了。
可以发现,跟Java创建线程明显不同的是:原生线程创建与运行是一体的,即线程创建完毕马上就运行。而Java中创建归创建,调用start线程才运行。
Java线程与原生线程之间是这样的关系:JavaThread->OSThread->native thread。后面会结合hotspot源码细讲。
Java线程创建
上一段创建Java线程的代码
从研究问题的角度,这段我们习以为常的代码要分成两部分来看:一、创建一个Java对象。注意,这一步只是单纯地创建一个Java对象,并没有什么特殊处理在里面。二、调用start方法让线程运行。我们上面提的几个问题,所有的秘密都在这一步中。接下来我结合hotspot源码将上面几个问题的答案分享给大家。
第一个问题:Java线程是如何与OS线程建立联系的,先上代码,这里只保留了关键代码
解释下上面的代码:
- 第2句创建了一个OSThread对象。第4句将JavaThread与OSThread建立联系。
- 第16句是创建原生线程。这一步执行完,新创建的线程就会马上执行java_start方法,java_start方法最终会通过JNI调用Java代码中的run方法。
- 第18句是将原生线程的ID存储到OSThread中。因为Linux下所有操作线程的API都需要传入线程ID。
- 总结来说,Java线程与原生线程之间是这样的关系:Thread对象->JavaThread->OSThread->native thread。
第二个问题:Java线程与OS线程共用一套线程状态吗?不是。JavaThread中有线程状态,OSThread也有线程状态。这个问题后面写篇文章细讲。
第三个问题:Java线程是如何做到创建与启动分开的?答案是借助锁。研究过Hotspot源码的小伙伴可能知道,这里的锁要么是parker对象, 要么是ParkEvent。这两个锁对象是理解多线程不可逾越的横沟,后面写篇文章细讲。
如果你对Java线程创建的细节能掌握到这个程度,理论上来说面试基本不会被虐。我再给你一些面试装叉的灵丹妙药。
分离线程
大家有没有注意到os::create_thread中的第8句代码。这句代码是设置即将创建的线程属性,值为PTHREAD_CREATE_DETACHED。这个值的意思是以分离状态创建线程。即我们通过new Thread创建的线程于OS而言都是分离线程。
为什么要创建分离线程,而不是普通线程呢?这就要说到分离线程的好处。也不得不感叹写JVM的大佬对硬件、对OS的精通程度。
在我们使用默认属性创建一个线程的时候,线程是 joinable 的。 joinable 状态的线程,必须在另一个线程中使用 pthread_join() 等待其结束, 如果一个 joinable 的线程在结束后,没有使用 pthread_join() 进行操作, 这个线程就会变成"僵尸线程"。每个僵尸线程都会消耗一些系统资源, 当有太多的僵尸线程的时候,可能会导致创建线程失败,因为每个进程能够持有的描述符是有限的。
当线程被设置为分离状态后,线程结束时,它的资源会被系统自动的回收, 而不再需要在其它线程中对其进行 pthread_join() 操作。
Linux系统层面的线程知识,我在我的手写JVM二期班中都会给大家补上。
我的困惑
Java的线程机制我觉得设计的过于复杂了,两个方面:一、需要维护两套线程状态;二、创建与启动分开。
目前不太理解JVM为什么要这样设计,所以我在手写JVM的课程中,我准备反其道而行,看看这样实现存在什么样的问题或者不便,这些问题或不便应该就是JVM这样设计的理由。这也就是我开设手写JVM小班的意义之一,你看到的所有理论,你心中的所有猜疑,如果你有一个自己手写自己熟悉的JVM,你就可以随时去论证,找到正确答案。而不是模棱两可、自己都没有底气的猜疑。
有些小伙伴可能想,我直接改hotspot源码不也可以吗?的确,可以,但是这个门槛太高了。你如果没有能力手写一个JVM,改hotspot源码那就是天方夜谭了。
题外话
子牙手写JVM小班四期招生即将结束。四期新增了字节码增强 Agent,学完你就可以做JVM相关的工作,如二开arthas,自研类hsdb调试器、自研实现热更新热部署零侵入日志等黑科技…
四期完整课程包含七大专题 一个增值专题,约50多个课时。完整学完你就可以:1、用Java写一个Java虚拟机,从而深入理解运行系统的底层细节;2、有能力自行研究Hotspot源码及其他用C语言、C 写的中间件源码;3、能够用C语言、C 写任何你感兴趣的基础算法如:内存池、垃圾回收算法、主从同步算法、执行引擎、存储引擎;4、就有底子跟着我学习下半年准备开的操作系统内核班……
这套课程,横跨多个计算机学科,但只是一个学科的价格。这套课程,JVM专家、功力深厚、经验丰富的子牙老师亲授,跟我学习不踩坑,全网唯一教授虚拟机的课程…
感兴趣小伙伴可以加班班微信咨询(jvm-anan),真诚招生,无任何套路。课程试看,问题真诚解答,全部了解清楚再上车。一二三期共500多VIP加入,无一人退费,好评不断