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

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

相关推荐
ttwuai18 分钟前
Go 后台接入 SSO 后菜单正常但接口 403,怎么排查权限链路?
开发语言·后端·golang
gugucoding20 分钟前
55. 【Java】Maven:项目构建的“管家”
java·maven
65岁退休Coder1 小时前
LangChain v1.3.4 笔记 - 08 MCP & 相关概念
后端·python·langchain
郑州光合科技余经理1 小时前
海外版多语言团购系统架构:主数据互通与核销边界
java·开发语言·前端·后端·系统架构·php·ai编程
Jesse_EC2 小时前
为什么我设置的 contentLength 到了消费端变成了 0?
java
vipxieliang2 小时前
ValidX 的 Date 和 DateTime vs JPA 的 Temporal 对比
java·后端
杜touch2 小时前
Spring框架的AnnotationConfigApplicationContext类启动的流程
java
触底反弹2 小时前
🔥 从「为什么」到「怎么用」:TypeScript 类型约束与泛型完全指南
后端·面试·typescript
用户8356290780512 小时前
使用 Python 查找和替换 Word 文档中的文本
后端·python