这是最新的大厂面试系列,还原真实场景,提炼出知识点分享给大家。

点赞再看,养成习惯~ 微信搜索【武哥聊编程】,关注这个 Java 菜鸟。

昨天有个小伙伴去阿里面试实习生岗位,面试官问他了一个老生常谈的问题:你说一说 Java 创建线程都有哪些方式?

这哥们心中窃喜,这个老生常谈的问题早已背的滚瓜烂熟,于是很流利的说了出来。

Java 创建线程有两种方式:

  1. 继承Thread类,并重写run()方法
  2. 实现Runnable接口,覆盖接口中的run()方法,并把Runnable接口的实现扔给Thread

面试官:(拿出一张白纸)那你把这两种方式写一下吧!

小哥:(这有何难!)好~

public static void main(String[] args) {
// 第一种
MyThread myThread = new MyThread();
myThread.start();
// 第二种
new Thread(() -> System.out.println("自己实现的run-2")).start();
} public static class MyThread extends Thread {
@Override
public void run() {
System.out.println("自己实现的run-1");
}
}

面试官:嗯,那除了这两种,还有其他创建线程的方法吗?

这些简单的问题难不倒这哥们,于是他想到了 Java5 之后的ExecutorsExecutors工具类可以用来创建线程池。

小哥:Executors工具类是用来创建线程池的,这个线程池可以指定线程个数,也可以不指定,也可以指定定时器的线程池,它有如下常用的方法:

newFixedThreadPool(int nThreads):创建固定数量的线程池

newCachedThreadPool():创建缓存线程池

newSingleThreadExecutor():创建单个线程

newScheduledThreadPool(int corePoolSize):创建定时器线程池

面试官:嗯,OK,咱们还是针对你刚刚写的代码,我再问你个问题。

此时这哥们有种不祥的预感,是不是自己代码写的有点问题?或者要问我底层实现?

面试官:你写的两种创建线程的方式,都涉及到了run()方法,你了解过Thread里的run()方法具体是怎么实现的吗?

果然问到了源码了,这哥们之前看了点,所以不是很慌,回忆了一下,向面试官道来。

小哥:emm……Thread 中的run()方法里东西很少,就一个 if 判断:

@Override
public void run() {
if (target != null) {
target.run();
}
}

有个target对象,会去判断该变量是否为空,非空的时候,去执行target对象中的run()方法,否则啥也不干。而这个target对象,就是我们说的Runnable

/* What will be run. */
private Runnable target;

面试官:嗯,那这个Runnable类你了解过吗?

小哥:了解过,这个Runnable类很简单,就一个抽象方法:

@FunctionalInterface
public interface Runnable {
public abstract void run();
}

这个抽象方法也是run()!如果我们使用Runnable接口,就需要实现这个run()方法。由于这个Runnable类上面标了@FunctionalInterface注解,所以可以使用函数式编程。

那小哥接着说:(突然自信起来了)所以这就对应了刚才说的两种创建线程的方式,假如我用第一种方式:继承了Thread类,然后重写了run()方法,那么它就不会去执行上面这个默认的run()方法了(即不会去判断target),会执行我重写的run()方法逻辑。

假如我是用的第二种方式:实现Runnable接口的方式,那么它会执行默认的run()方法,然后判断target不为空,再去执行我在Runnable接口中实现的run()方法。

面试官:OK,可以,我再问你个问题。

小哥:(暗自窃喜)

面试官:那如果我既继承了Thread类,同时我又实现了Runnable接口,比如这样,最后会打印什么信息出来呢?

面试官边说边拿起刚刚这小哥写的代码,对它进行了简单的修改:

public static void main(String[] args) {
new Thread(() -> System.out.println("runnable run")) {
@Override
public void run() {
System.out.println("Thread run");
}
}.start();
}

这小哥,突然有点懵,好像从来没想过这个问题,一时没有什么思路,于是回答了个:会打印 “Thread run” 吧……

面试官:答案是对的,但是为什么呢?

这小哥一时没想到原因,于是面试官让他回去可以思考思考,就继续下一个问题了。

亲爱的读者朋友,你们知道为什么吗?你们可以先思考一下。

其实这个答案很简单,我们来分析一下代码便知:其实是 new 了一个对象(子对象)继承了Thread对象(父对象),在子对象里重写了父类的run()方法;然后父对象里面扔了个Runnable进去,父对象中的run()方法就是最初那个带有 if 判断的run()方法。

好了,现在执行start()后,肯定先在子类中找run()方法,找到了,父类的run()方法自然就被干掉了,所以会打印出:Thread run。

如果我们现在假设子类中没有重写run()方法,那么必然要去父类找run()方法,父类的run()方法中就得判断是否有Runnable传进来,现在有一个,所以执行Runnable中的run()方法,那么就会打印:Runnable run 出来。

说白了,就是问了个 Java 语言本身的父子继承关系,会优先执行子类重写的方法而已,只是借这个场景,换了个提问的方式,面试者可能一时没反应过来,有点懵也是正常的,如果直接问,傻子都能回答的出来。

后记:通过这道简单的面试题,帮大家分析了一下在创建线程过程中的源码,可以看出来,面试过程中,面试官更加看重一些原理性的东西,而不是背一下方式就行了。同时也能看的出,面试大厂,需要做好充分的准备。另外,在面试的时候要冷静,可能有些问题并没有太难,回答不出来只是当时太紧张造成的。

这篇文章就写到这,最后祝大家都能面试成功。

如果觉得有帮助,希望老铁们来个三连击,给更多的人看到这篇文章

1、关注我的原创微信公众号「武哥聊编程」,专注于Java、数据结构和算法、微服务、中间件等技术分享,保证你看完有所收获。

2、给俺点个赞呗,可以让更多的人看到这篇文章,顺便激励下我继续写作,嘻嘻。

当阿里面试官问我:Java创建线程有几种方式?我就知道问题没那么简单的更多相关文章

  1. Java创建线程的四种方式

    Java创建线程的四种方式 1.继承Thread类创建线程 定义Thread类的子类,并重写该类的run方法,run()方法的内容就是该线程执行的内容 创建Thread子类的实例,即创建了线程对象. ...

  2. java创建线程的三种方式及其对照

    Java中创建线程主要有三种方式: 一.继承Thread类创建线程类 (1)定义Thread类的子类.并重写该类的run方法,该run方法的方法体就代表了线程要完毕的任务.因此把run()方法称为运行 ...

  3. Java并发编程:Java创建线程的三种方式

    目录 引言 创建线程的三种方式 一.继承Thread类 二.实现Runnable接口 三.使用Callable和Future创建线程 三种方式的对比 引言 在日常开发工作中,多线程开发可以说是必备技能 ...

  4. java创建线程的三种方式及其对比

    第一种方法:继承Thread类,重写run()方法,run()方法代表线程要执行的任务.第二种方法:实现Runnable接口,重写run()方法,run()方法代表线程要执行的任务.第三种方法:实现c ...

  5. AJPFX总结java创建线程的三种方式及其对比

    Java中创建线程主要有三种方式: 一.继承Thread类创建线程类 (1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务.因此把run()方法称为执行 ...

  6. Java创建线程的第二种方式:实现runable接口

    /*需求:简单的卖票程序多个窗口买票 创建线程的第二种方式:实现runable接口 *//*步骤1.定义类实现Runable接口2.覆盖Runable接口中的run方法    将线程要运行的代码存放在 ...

  7. java创建线程的两种方式及源码解析

    创建线程的方式有很多种,下面我们就最基本的两种方式进行说明.主要先介绍使用方式,再从源码角度进行解析. 继承Thread类的方式 实现Runnable接口的方式 这两种方式是最基本的创建线程的方式,其 ...

  8. Java创建线程的三种方式

    一.继承Thread类创建线程类 (1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务.因此把run()方法称为执行体. (2)创建Thread子类的实 ...

  9. java创建线程的几种方式,了解一下

    1.继承Thread,重写run()  public class MyThread extends Thread{ @Override public void run() { System.out.p ...

随机推荐

  1. call 和 apply 和 bind的区别

    有些东西说忘就往,每天记录自己忘记的东西重新学习一遍,挺好 作用:call()和apply()用法都是一样的,改变this的指向问题 区别:接收参数的方式不同, (bind 方法是附加在函数调用后面使 ...

  2. IntelliJ IDEA 2020 全家桶注册码

    WU78YHTY7E-eyJsaWNlbnNlSWQiOiJPUVQzT0oyNVhFIiwibGljZW5zZWVOYW1lIjoi5rC45LmF5r+A5rS7IGlkZWEubWVkZW1pb ...

  3. Iterm2 快捷操作

    窗口操作 新建窗口:Command + N 关闭所有窗口:Shift + Command + W 窗口之间切换 前一个窗口: Command + [ 后一个窗口:Command + ] 进入窗口1,2 ...

  4. Java自学路线图之Java系统自学

    Java自学不是一朝一夕的事情.可以采用"懒开始"的方法,但是必须要坚持下去,才能真正自学Java掌握编程技术.那些企图学几天去包装一下找工作的,请绕道.如果你下定决心自学Java ...

  5. C++ 随笔练习

    //例题:求Sn=a+aa+aaa+…+aa…aaa(有n个a)之值,其中a是一个数字,为2. 例如,n=5时=2+22+222+2222+22222,n由键盘输入.//题目来源:https://ww ...

  6. table 上下左右 4根线的写法 :before :after 他们就能把td里面右下的那颗线给盖上 还有body和header横向滚动的联动 || 不能把body套在header上是为了上header表头固定 || 还有表头header的右侧overflow-y 是否出现滚动条的位置 记得有一个$nextTick 要不然会获取不到高度 高度就为0了 || 横向滚动条纵向滚动条

    table 上下左右 4根线的写法 <!--* @description 重点查核人员表!--><template> <div class="keyChecke ...

  7. 【Weiss】【第03章】练习3.19:计算后缀表达式

    [练习3.19] 编写一个程序计算后缀表达式的值. Answer: 计算的方法书上说得很明白了,看代码行,没写错误检测[因为懒]. 测试代码: #include <iostream> #i ...

  8. 12.unittest的学习

    unittest学习后的总结,记录各个主要内容

  9. Linux & Shell 学习笔记【1/2】

    因为工作上的需要,花了些许时间去熟悉学习Linux和Shell,现在也花点事件在此记录一下以加强巩固学习的内容吧.学的不算深入,所以都是一些比较junior的内容. 在下一篇随笔会详述之前写的一个用于 ...

  10. java实现SSO(SingleSignOn)单点登录服务

    单点登录SSO:是指用户通过一次登录,可以访问任意所有相互信任的应用系统.即一处登录,处处登录.比如阿里系下的淘宝.天猫等,虽然是不同的产品,但归于一个体系下,是可以相互信任的应用系统. 为了方便用户 ...