跟小白一起领悟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成为定义任务的顶级接口,也有助于逻辑的实现。

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

相关推荐
MC丶科2 小时前
【SpringBoot常见报错与解决方案】中文乱码?Spring Boot 统一解决前后端中文乱码问题(含 Postman 测试)!别再百度“加 UTF-8”了!
spring boot·后端·postman
kk哥88995 小时前
如何快速掌握JavaSE的核心语法?
java
我是一只小青蛙8885 小时前
AVL树:平衡二叉搜索树原理与C++实战
java·jvm·面试
浩瀚地学5 小时前
【Java】JDK8的一些新特性
java·开发语言·经验分享·笔记·学习
XXOOXRT6 小时前
基于SpringBoot的加法计算器
java·spring boot·后端·html5
阿崽meitoufa6 小时前
JVM虚拟机:垃圾收集器和判断对象是否存活的算法
java·jvm·算法
我是苏苏7 小时前
C#高级:使用ConcurrentQueue做一个简易进程内通信的消息队列
java·windows·c#
moxiaoran57537 小时前
Go语言的错误处理
开发语言·后端·golang
heartbeat..8 小时前
数据库基础知识体系:概念、约束、范式与国产产品
java·数据库·学习笔记·国产数据库