这是最新的大厂面试系列,还原真实场景,提炼出知识点分享给大家。 除此之外,文末有粉丝血赚福利哦!
昨天有个小伙伴去阿里面试实习生岗位,面试官问他了一个老生常谈的问题:你说一说 Java 创建线程都有哪些方式?
这哥们心中窃喜,这个老生常谈的问题早已背的滚瓜烂熟,于是很流利的说了出来。
Java 创建线程有两种方式:
1. 继承Thread类,并重写run()方法
2. 实现Runnable接口,覆盖接口中的run()方法,并把Runnable接口的实现扔给Thread
面试官:(拿出一张白纸)那你把这两种方式写一下吧!
小哥:(这有何难!)好~
publicstaticvoidmain(String[]args){// 第一种MyThreadmyThread=newMyThread();myThread.start();// 第二种newThread(()->System.out.println("自己实现的run-2")).start();}publicstaticclassMyThreadextendsThread{@Overridepublicvoidrun(){System.out.println("自己实现的run-1");}}
面试官:嗯,那除了这两种,还有其他创建线程的方法吗?
这些简单的问题难不倒这哥们,于是他想到了 Java5 之后的Executors,Executors工具类可以用来创建线程池。
小哥:Executors工具类是用来创建线程池的,这个线程池可以指定线程个数,也可以不指定,也可以指定定时器的线程池,它有如下常用的方法:
newFixedThreadPool(int nThreads):创建固定数量的线程池
newCachedThreadPool():创建缓存线程池
newSingleThreadExecutor():创建单个线程
newScheduledThreadPool(int corePoolSize):创建定时器线程池
面试官:嗯,OK,咱们还是针对你刚刚写的代码,我再问你个问题。
此时这哥们有种不祥的预感,是不是自己代码写的有点问题?或者要问我底层实现?
面试官:你写的两种创建线程的方式,都涉及到了run()方法,你了解过Thread里的run()方法具体是怎么实现的吗?
果然问到了源码了,这哥们之前看了点,所以不是很慌,回忆了一下,向面试官道来。
小哥:emm……Thread中的run()方法里东西很少,就一个 if 判断:
@Overridepublicvoidrun(){if(target!=null){target.run();}}
有个target对象,会去判断该变量是否为空,非空的时候,去执行target对象中的run()方法,否则啥也不干。而这个target对象,就是我们说的Runnable:
/* What will be run. */privateRunnabletarget;
面试官:嗯,那这个Runnable类你了解过吗? 小哥:了解过,这个Runnable类很简单,就一个抽象方法:
@FunctionalInterfacepublicinterfaceRunnable{publicabstractvoidrun();}
这个抽象方法也是run()!如果我们使用Runnable接口,就需要实现这个run()方法。由于这个Runnable类上面标了@FunctionalInterface注解,所以可以使用函数式编程。
那小哥接着说:(突然自信起来了)所以这就对应了刚才说的两种创建线程的方式,假如我用第一种方式:继承了Thread类,然后重写了run()方法,那么它就不会去执行上面这个默认的run()方法了(即不会去判断target),会执行我重写的run()方法逻辑。
假如我是用的第二种方式:实现Runnable接口的方式,那么它会执行默认的run()方法,然后判断target不为空,再去执行我在Runnable接口中实现的run()方法。
面试官:OK,可以,我再问你个问题。
小哥:(暗自窃喜)
面试官:那如果我既继承了Thread类,同时我又实现了Runnable接口,比如这样,最后会打印什么信息出来呢?
面试官边说边拿起刚刚这小哥写的代码,对它进行了简单的修改:
publicstaticvoidmain(String[]args){newThread(()->System.out.println("runnable run")){@Overridepublicvoidrun(){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 语言本身的父子继承关系,会优先执行子类重写的方法而已,只是借这个场景,换了个提问的方式,面试者可能一时没反应过来,有点懵也是正常的,如果直接问,傻子都能回答的出来。
如果觉得有帮助,希望老铁们来个点赞关注我,这是我创作的最强动力!除此之外下方分享一波我的面试宝典~
分享一波我的面试宝典
21天啃完283页的pdf文档
Java部分:Java基础,集合,并发,多线程,JVM,设计模式
数据结构算法:Java算法,数据结构
开源框架部分:Spring,MyBatis,MVC,netty,tomcat
分布式部分:架构设计,Redis缓存,Zookeeper,kafka,RabbitMQ,负载均衡等
微服务部分:SpringBoot,SpringCloud,Dubbo,Docker
资料获取方式:点赞和转发这篇文章,然后关注小编,后台私信【面试资料】即可打包带走所有资料~
2019年一线互联网企业350道面试答案整理
性能优化面试专栏
微服务架构面试专栏
并发编程高级面试专栏
开源框架面试题专栏
分布式面试专栏
大厂的面试场景
资料获取方式:点赞转发这篇文章,然后关注小编,后台私信【面试资料】即可打包带走所有资料~
1、面试文档专题整理
既然是要面试,那么就少不了刷题,实际上春节回家后,哪儿也去不了,我自己是刷了不少面试题的,所以在面试过程中才能够做到心中有数,基本上会清楚面试过程中会问到哪些知识点,高频题又有哪些,所以刷题是面试前期准备过程中非常重要的一点。
根据自身面试经历整理以及不断收集的(珍藏版)
相关的电子书、底层源码
阿里巴巴必备学习知识点
结束语
对于大厂面试,我最后想要强调的一点就是心态真的很重要,是决定你在面试过程中发挥的关键,若不能正常发挥,很可能就因为一个小失误与offer失之交臂,所以一定要重视起来。另外提醒一点,充分复习,是消除你紧张的心理状态的关键,但你复习充分了,自然面试过程中就要有底气得多。
以上内容中所有的学习资料、面试资料,均可以免费提供,希望大家金三银四面试顺利,拿下自己心仪的offer!
资料获取方式:转发和评论这篇文章,然后关注小编,后台私信【面试资料】即可打包带走所有资料~
网友评论