跟小白一起领悟Thread——如何开启一个线程(上)

校招面试回忆录:请说出在Java中如何开启一个线程?

上述两张图来自jdk1.8Thread类的注释,真有点八股文答案的意思了。。。。。 总结下来就是:

  • 要么继承Thread类并实现它的run方法
  • 要么实现Runnable接口,在构造Thread的时候当作参数传进去。

但是仔细思考发现Thread类本身是实现Runnable接口的,那么其实方法一本质也是方法二的变体;这时候有人就会说还有一种创建线程方式------Callable接口;确实是的,在提交线程池任务的时候,为了同时满足异步和获取结果,我们通常将任务包装成Callable丢给线程池,线程池底层使用的Thread。但是当我们仔细寻找Thread类中有没有Callable接口的蛛丝马迹,结果也是找不到的,原因也很简单,如下图所示:

实际上,我们给线程池提交的Callable接口被newTaskFor方法转换成了Ruunbale接口的子类RunnableFuture对象,根据做里氏替换原则,在构造Thread的时候是可以当作参数传进去的。 好像分析到这最终的答案也比较明确了,真相只有一个,实现Runnable接口。 但是,根据我自己的思考,我认为实现Runnable接口,好像并不是创建或者说开启一个线程的关键。因为实现runnable,继承Thread并创建对象也罢,这些行为都并不是发生在要创建的线程中,那么真正发生在创建的线程中的行为应该是执行了Runnable接口run方法的线程;那么触发运行执行了Runnable接口run方法的操作就是Thread的start方法,对该方法追根溯源会到一个jni方法:

也就是说,调用Thread的start方法会调用start0这个jni方法,从而与jvm层面实现的方法进行交互,jvm作为一款虚拟机提供了独立且完整的虚拟计算机环境,那么在Java语言层面的Thread也会映射到jvm的线程管理概念,然后jvm再去将jvm线程概念映射到操作系统内核线程上,这样才能实现真正的被操作系统调度从而获取cpu执行的条件。总的来讲,Java语言层面的Thread被称作用户态线程,jvm负责对该用户态线程与内核态线程进行映射,从而获取被调度的能力。所以我给出的答案是Java中如何开启一个线程是调用Thread对象的start方法,与jvm虚拟机进行交互,实现"注册"和"委托"映射到内核态线程,这里利用jstack来看下:

线程ID:#11=====>线程ID(JVM):tid=0x000000015d066000=====>本地线程ID:nid=0x5803,在这里java的Thread其实到内核态线程映射转换了两次。

也就是说,Runnable的run方法实现逻辑以及Callable的call方法实现逻辑(被封装为FutureTask类后,其实也是在run方法内调用call方法)其实更多的是定义了要执行的任务是什么,而不是创建一个新线程;

换个角度思考,线程池通过对线程的池化,不断的复用Thread,干完一个任务(Runnable)还是会干下一个任务(Runnable)的,不会说是来一个任务,就要创建一个新的线程去做;以下是线程池中线程的run方法调用的runWorker方法,不断的getTask并运行:

那为什么Thread不能直接接受Callable而是要包装成Runnable呢?从源码的注释可以得到一些信息,Thread和Runnable是since jdk1.0的,而Callable是由大神Doug Lea在jdk1.5提交的,在这个jdk版本,Doug Lea还提交了线程池系列的源码,也就是说Callable是线程池生态圈时期诞生的,领先于Thread和Runnable的发行时期,这里笔者大胆推断是更好管理,让Runnable成为定义任务的顶级接口,也有助于逻辑的实现。

面试官:你这回答的和我一分钟前搜的答案不太一样呀,还是回去等通知吧!

相关推荐
Victor35617 分钟前
Redis(50) Redis哨兵如何与客户端进行交互?
后端
程序员爱钓鱼20 分钟前
Go语言实战案例-开发一个JSON格式校验工具
后端·google·go
零千叶21 分钟前
【面试】AI大模型应用原理面试题
java·设计模式·面试
坐吃山猪5 小时前
SpringBoot01-配置文件
java·开发语言
我叫汪枫5 小时前
《Java餐厅的待客之道:BIO, NIO, AIO三种服务模式的进化》
java·开发语言·nio
yaoxtao5 小时前
java.nio.file.InvalidPathException异常
java·linux·ubuntu
M1A16 小时前
小红书重磅升级!公众号文章一键导入,深度内容轻松入驻
后端
Swift社区7 小时前
从 JDK 1.8 切换到 JDK 21 时遇到 NoProviderFoundException 该如何解决?
java·开发语言
0wioiw07 小时前
Go基础(④指针)
开发语言·后端·golang