一锤定音
Oracle官网的文档中:
实现多线程的方式一共有两种:
- 方法一:实现Runnable接口
- 方法二:继承Thread类
官方文档
方法一:实现Runnable接口
1 | /** |
输出结果:
1 | 用Runnable方法实现线程 |
方法二:继承Thread类
1 | /** |
输出结果:
1 | "用Thread类实现线程" |
两种方法的比较
- 实现Runnable接口的方式更好
- 方法1的解耦效果好
- 实现Thread,创建、销毁线程损耗大,实现Runnable可以使用线程池的技术减少损耗
- java只支持单一继承,继承Thread类占据了继承
- 本质区别
- 方法一:最终调用了target.run()
- 假设A类实现了Runnable接口,然后实现了run方法。
- 在使用 new Thread(new A())进行创建线程的时候,A类是作为Runnable类型的参数被传入的,根据Thread源码,Thread的run()方法,有一个Runnable类型的参数,就是在new Thread时传入的,如果这个参数不为空,就直接使用这个参数(A类)的run方法
1 | //Thread类使用Runnable接口初始化线程方法,调用init方法 |
- 方法二:run()方法整个都被重写
- 继承Thread类的方式,会重写run()方法,就直接覆盖了上面源码中Thread类的run方法,相当于抛弃了Thread类自带的run方法,就只是执行自己写的run方法了。
同时使用两种办法会怎么样
1 | public class BothRunnableThread { |
最终结果:
1 | 我来自Thread,重写Thread的run方法 |
原因:
- 在new Thread的时候,传入了Runnable对象,并同时重写了Thread的run方法,重写了run方法后,就没有Thread的这三行代码了,那么Runnable对象的run方法就自然不会执行了
1
2
3
4
5public void run() {
if (target != null) {
target.run();
}
}
总结
- 通常来说,共有两种方式创建线程,Oracle官方也是这么说的
- 准确来讲,创建线程只有一种方式,那就是构造Thread类,而实现线程的执行单元有两种方式
- 方法一:实现Runnable接口的run方法,并把Runnable实例传给Thread类
- 方法二:重写Thread类的run方法(继承Thread类)
典型错误观点
- 线程池创建线程也算是一种新建线程的方式
- 线程池本质也是传入的Runnable方式创建的线程
- 通过Clallable和FutureTask创建线程,也算是一种新建线程的方式
- 根据UML图,它们也都是使用了两种方式实现的
参考
- 本文作者: xczll
- 本文链接: https://xczllgit.github.io/2020/03/09/concurrent/2020-03-09-multiThread-build/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!