1. 实现多线程的正确姿势

发布于 2022年 02月 13日 15:01

此笔记使用wolai进行梳理,欢迎来访,指出问题 1核心一:实现多线程的正确姿势 www.wolai.com/mucong/jiGM…

1.1究竟如何创建新线程

网上说法:1种,2种,3种、、、、

  • 说法不统一,那么究竟是多少种呢?

Oracle官方解答:2种

  1. 实现Runnable接口创建线程

  2. 继承Thread创建线程

思考与总结:

为什么是两种方法而不是更多,通过一下五个方面进行阐述

  1. 阐明叙事角度:从不同的角度看,会有不同的答案。

  2. 两种方法实现,分别是实现Runnable接口和继承Thread类

  3. 我们看原理,其实Thread类实现了Runnable接口,并且看Thread类的run方法,会发现其实那两种本质都是一样的,run方法的代码如下

  4. 方法一和方法二,也就是“继承Thread类然后重写run()”和“实现Runnable接口并传入Thread类”在实现多线程的本质上,并没有区别,都是最终调用了start()方法来新建线程。这两个方法的最主要区别在于run()方法的内容来源: 方法一:最终调用target.run(); 方法二:run()整个都被重写

  5. 还有其他的实现线程的方法,例如线程池等,它们也能新建线程,但是细看源码,从没有逃出过本质,也就是实现Runnable接口和继承Thread类

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

结论:我们只能通过新建Thread类这一种方式来创建线程,但是类里面的run方法有两种方式来实现,第一种是重写run方法,第二种实现Runnable接口的run方法,然后再把该Runnable实例传给Thread类。除此之外,从表面上看线程池、定时器等工具类也可以创建线程,但是它们的本质都逃不出刚才所说的范围。

思考:二者的使用优先级(当然是Runnable啦

  • 从三个角度分析
  1. 从代码架构方面解释:具体的任务(run方法)应该与"创建和运行线程的机制(Thread类)”解耦,用runnable对象可以实现解耦合

  2. 如果使用继承,我们每次新建一个任务,都要独立的新建一个线程,消耗较大(比如冲头开始创建一个线程、执行完毕以后再销毁等。如果线程的实际工作内容,也就是run()函数里只是简单的打印一行文字的话,那么可能线程的实际工作内容还不如损耗来的大)。而使用Runnable和线程池,就可以大大减小这样的损耗

  3. 由于Java是单继承多实现的,继承少用


1.2两种方式同时使用会怎么样?

  • 代码如下
/**
 * Created by mucong on 2021/1/10 16:30
 * 描述:        同时使用Runnable与Thread方法创建线程会怎么样
 */
public class BothRunnableThread {

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("我是Runnable创建的线程");
            }
        }){
            @Override
            public void run() {
                System.out.println("我是Thread创建的线程");
            }
        }.start();
    }
}

现象如下:

  • 结果:Thread与Runnable同时创建线程并调用run方法会丢失Runnable的run方法

  • 原因:由1.1中提到,创建线程就只有两种方法,而使用Thread的run方法相当于覆盖了父类的run方法,结果会导致Runnable无法调用底层run方法的内容。


1.3典型错误观点剖析

还未完成整理

  1. "线程池创建线程也算是一种新建线程的方式"

  2. "通过Callable和FutureTask创建线程,也算是一种新的创建线程的方式"

  3. "无返回值是实现runnable接口,有返回值是Callable接口,所以callable是新的实现线程的方式"

  4. 定时器

  5. 匿名内部类

  6. Lambda表达式


1.4常见面试题

有多少种实现线程的方法?

实现Runnable接口与继承Thread类哪种方式更好?

推荐文章