面试官唬我半天后问:创建线程有几种方式?我......

前言

1024节日快乐!!!

在这里插入图片描述

多线程在面试中基本上已经是必问项了,面试官通常会从简单的问题开始发问,然后再一步一步的挖掘你的知识面。本文稍微有点长,耐心阅读!

比如,从线程是什么开始,线程和进程的区别,创建线程有几种方式,线程有几种状态,等等。

接下来自然就会引出线程池,Lock,Synchronized,JUC的各种并发包。然后就会引出 AQS、CAS、JMM、JVM等偏底层原理,一环扣一环。

这一节我们不聊其他的,只说创建线程有几种方式。

是不是感觉非常简单,不就是那个啥啥那几种么。

其实不然,只有我们给面试官解释清楚了,并加上我们自己的理解,才能在面试中加分。

正文

一般来说我们比较常用的有以下四种方式,下面先介绍它们的使用方法。然后,再说面试中怎样回答面试官的问题比较合适。

1、继承 Thread 类

通过继承 Thread 类,并重写它的 run 方法,我们就可以创建一个线程。

首先定义一个类来继承 Thread 类,重写 run 方法。
然后创建这个子类对象,并调用 start 方法启动线程。
在这里插入图片描述
2、实现 Runnable 接口
通过实现 Runnable ,并实现 run 方法,也可以创建一个线程。

首先定义一个类实现 Runnable 接口,并实现 run 方法。
然后创建 Runnable 实现类对象,并把它作为 target 传入 Thread 的构造函数中
最后调用 start 方法启动线程。
在这里插入图片描述
3、实现 Callable 接口,并结合 Future 实现
首先定义一个 Callable 的实现类,并实现 call 方法。call 方法是带返回值的。
然后通过 FutureTask 的构造方法,把这个 Callable 实现类传进去。
把 FutureTask 作为 Thread 类的 target ,创建 Thread 线程对象。
通过 FutureTask 的 get 方法获取线程的执行结果。
在这里插入图片描述
4、通过线程池创建线程
此处用 JDK 自带的 Executors 来创建线程池对象。

首先,定一个 Runnable 的实现类,重写 run 方法。
然后创建一个拥有固定线程数的线程池。
最后通过 ExecutorService 对象的 execute 方法传入线程对象。
在这里插入图片描述
到底有几种创建线程的方式?
那么问题来了,我这里举例了四种创建线程的方式,是不是说明就是四种呢?

我们先看下 JDK 源码中对 Thread 类的一段解释,如下图。
在这里插入图片描述

There are two ways to create a new thread of execution

翻译: 有两种方式可以创建一个新的执行线程

这里说的两种方式就对应我们介绍的前两种方式。

但是,我们会发现这两种方式,最终都会调用 Thread.start 方法,而 start 方法最终会调用 run 方法。

不同的是,在实现 Runnable 接口的方式中,调用的是 Thread 本类的 run 方法。我们看下它的源码,
在这里插入图片描述
这种方式,会把创建的 Runnable 实现类对象赋值给 target ,并运行 target 的 run 方法。

再看继承 Thread 类的方式,我们同样需要调用 Thread 的 start 方法来启动线程。由于子类重写了 Thread 类的 run 方法,因此最终执行的是这个子类的 run 方法。

所以,我们也可以这样说。在本质上,创建线程只有一种方式,就是构造一个 Thread 类(其子类其实也可以认为是一个 Thread 类)。

而构造 Thread 类又有两种方式,一种是继承 Thread 类,一种是实现 Runnable接口。其最终都会创建 Thread 类(或其子类)的对象。

再来看实现 Callable ,结合 Future 和 FutureTask 的方式。可以发现,其最终也是通过 new Thread(task) 的方式构造 Thread 类。

最后,在线程池中,我们其实是把创建和管理线程的任务都交给了线程池。而创建线程是通过线程工厂类 DefaultThreadFactory 来创建的(也可以自定义工厂类)。我们看下这个工厂类的具体实现。
在这里插入图片描述
它会给线程设置一些默认值,如线程名称,线程的优先级,线程组,是否是守护线程等。最后还是通过 new Thread() 的方式来创建线程的。

因此,综上所述。在回答这个问题的时候,我们可以说本质上创建线程就只有一种方式,就是构造一个 Thread 类。(此结论借鉴来源于 Java 并发编程 78 讲 – 徐隆曦)

个人瞎比比

但是,在这里我想对这个结论稍微提出一些疑问(若有不同见解,文末可留言交流~)。。。

个人认为,如果你要说有 1种、2种、3种、4种 其实也是可以的。重要的是,你要能说出你的依据,讲出它们各自的不同点和共同点。讲得头头是道,让面试官对你频频点头。。

说只有构造 Thread 类这一种创建线程方式,个人认为还是有些牵强。因为,无论你从任何手段出发,想创建一个线程的话,最终肯定都是构造 Thread 类。(包括以上几种方式,甚至通过反射,最终不也是 newInstance 么)。

那么,如果按照这个逻辑的话,我就可以说,不管创建任何的对象(Object),都是只有一种方式,即构造这个对象(Object) 类。这个结论似乎有些太过无聊了,因为这是一句非常正确的废话。

以 ArrayList 为例,我问你创建 ArrayList 有几种方式。你八成会为了炫耀自己知道的多,跟我说,

  • 通过构造方法,List list = new ArrayList();
  • 通过 Arrays.asList(“a”, “b”);
  • 通过Java8提供的Stream API,如 List list = Stream.of(“a”,“b”).collect(Collectors.toList());
  • 通过guava第三方jar包,List list3 = Lists.newArrayList(“a”, “b”);

等等,仅以上就列举了四种。现在,我告诉你创建 ArrayList 就只有一种方式,即构造一个 ArrayList 类,你抓狂不。

这就如同,我问你从北京出发到上海去有几种方式。

你说可以坐汽车、火车、坐动车、坐高铁,坐飞机。

那不对啊,动车和高铁都属于火车啊,汽车和火车都属于车,车和飞机都属于交通工具。这样就是只有一种方式了,即坐交通工具。

这也不对啊,我不坐交通工具也行啊,我走路过去不行么(我插眼传送也可以啊~)。

最后结论就是,只有一种方式,那就是你人到上海即可。这这这,这算什么

所以个人认为,说创建线程只有一种方式有些欠妥。好好的一个技术文,差一点被我写成议论文了。。。

这个仁者见仁智者见智吧。

最后

提莫在这里也还整理了一些平时自己学习的技术文档与群友平时去面试的面试资料。
如果看完后对你有帮助,记得点赞支持一下哦!

Ps:有需要的小伙伴可以点击进入暗号:csdn,免费获取。

面试专题文档。

在这里插入图片描述

技术文档

在这里插入图片描述

真实大厂面经

在这里插入图片描述

今天的吹牛逼到此结束,一起交流,一起进步,加油,打工人!!!

猜你喜欢

转载自blog.csdn.net/qq_41770757/article/details/109260578