实现多线程的方法(附Oracle官方解释以及面试回答方法)
一、实现多线程究竟有多少种方法
百度一下有很多不同的文章,有说一种的有说两种的也有说四种的,这让很多不了解多线程想要入门的猿猿们很头疼。
上面是2020年1月8号搜索的百度,可以看到如果没有了解并且想要了解的猿猿们如果看到这些会立马丧失大半的学习激情吧,本文没有批判其他文章的作者的意思,仅仅通过官方的文档最直接了当的告诉大家正确的思路以及使用方法。
正确答案是:两种
在回答面试的时候,我们可以说一种:“新建Thread类,两种run()方法的实现方式来实现多线程创建”,然后细分为两种,后面有介绍,细品。
可以看到官方文档上已经给出了明确答案,下面我们将围绕这两种方法进行展开分析:
- 继承Thread类,重写run()方法。
- 实现Runnable接口,实现run()方法。
二、实现Runnable接口
截取了自己的代码片段来做讲解
- 首先我们自己创建一个RunnableStyle类来实现Runnable接口,要求我们重写run方法,我们再run方法中打印。
- new Thread将我们新创建的类作为参数传入,调用start方法开启线程。
我们通过IDEA的快捷键(ctrl+F12)查看Thread中的run方法。
我们可以看到重写了run方法,如果target不等于null,那么执行run。
那么target是什么呢,我们再看一张图。
通过源码我们可以看到target就是我们的Runnable类。
所以我们看到在执行Thread thread = new Thread(new RunnableStyle());
这个代码时Thread类会先判断是否为null。然后执行。
二、继承Thread
同样截取了代码片段供大家参考:
代码看起来比实现Runnable接口少一点,不用newThread()传入参数。但通通过上面源码我们可以了解到,继承了父类那么子类重写父类run()方法时,必将覆盖父类方法。也就是说不等于null的判断被我们的程序(代码中是打印)取代。在某些角度来说,是不好的。 所以给大家总结一下两种实现多线程方法具体的优缺点以及区别之处。
三、两种方法的比较
大家仔细阅读,相信对理解有很大的帮助
- 继承
Thread
方法是不推荐的,推荐我们创建多线程时使用实现Runnable
的方法,原因有一下几点:
- 从代码架构角度来说:具体的任务(run方法)应该跟“创建跟运行线程的机制(也就是Thread类)”解耦,用runnable对象可以实现解耦。
- 大量使用Thread类会提高损耗,因为只要想新建一个任务就要新建一个线程。而不如用线程池和runnable对象损耗低。
- 继承Thread方法后我们没办法再继承别的方法了,因为java不支持双继承。程序拓展性变低。
以上总结而言:推荐使用实现Runnable接口的方法来创建新的线程。
面试回答:
做几点补充:
- 肯定有同学会否定创建线程的两种方式,其实我想说想现在大众化理解的创建多线程的方法与本文有很大的出入,比如:定时器,lambda表达式,线程池等等都可以创建新线程,这么说也没错,但是看过源码不难发现最终都会去实现Runnable这个类。随着技术的发展与迭代,还会有越来越多的包装类、甚至像lambda表达式这样新的语法,更简单更便捷的去实现一个线程的创建,但万变不离其宗吧。本文也是让一些从没接触过多线程的同学可以有一个系统的了解以及面试更具体的答出面试官想要的答案。
- start()方法的使用,当一个线程对象创建好之后,我们会通过
.start()
方法去启动而不是run()。但是同一个线程并不能启动两次,也就是说调用两次start方法。我们从源码的角度出发。
在执行start方法时会检测当前线程的状态,如果不是初始化的0,则会报错
以上便是线程的创建以及启动的各种知识点吧,希望对大家有帮助,如果本人有不对的地方,或者各位有不同的见解,欢迎在评论区提出来。我们共同探讨。