关于创建线程的方式有几种详解编程语言

导语

在我们平常的面试中,我们经常会被面试官问道:你知道Java中多线程的实现方式有几种吗?在网上刷过面试题的童鞋们,一般都会说两种:继承Thread、实现Runnable,甚至还有甚者可能会说三种、四种的,对于说三种四种的一般都是把什么ExecutorService什么的包含在内了。

这时可能有人就想了,既然这么说,那么到底是几种呢?那么本篇文章就将会从不同的角度来和大家说明一下,在我们日常的Java开发中,这实现多线程的方式到底有几种。

 

一、关于官方的解答

在正式开始之前,我们先来看下Oracle官网是如何表述的,那就来给大家看两段段话:

关于创建线程的方式有几种详解编程语言

关于创建线程的方式有几种详解编程语言

对于上方的两段话的基本意思就是:有两种方法可以创建新的执行线程。一种是将类声明为子类Thread,该子类需要重写run方法,另一种是实现Runnable接口。说到这里那些在面试中说了这两种的童鞋便会非常的自豪,当然的确如此,因为这是官方文档所阐述的,这也是我们要在学习的时候要以官方文档为主,而不是网上的一些博客,尤其是一些复制来复制去的博客。当然,也可以嫌弃我的博客,因为博主也是只是有限。

对于官网所说的两种实现方式,这里简单的用代码演示:

public class RunnableDemo implements Runnable { 
    public static void main(String[] args) { 
        Thread thread = new Thread(new RunnableDemo()); 
        thread.start(); 
    } 
    @Override 
    public void run() { 
        System.out.println("实现Runnable方式!"); 
    } 
} 
 
public class ThreadDemo extends Thread { 
    @Override 
    public void run() { 
        System.out.println("继承Thread方法!"); 
    } 
 
    public static void main(String[] args) { 
        new ThreadDemo().start(); 
    } 
}

这里我抛个疑问官网上面的第一段的第一句话,给你真正的理解是什么?

二、从源码来看看

从上面的代码中,我们可以看到的就是继承Thread,它是重写了run方法的,就相当于我们在调用start方法的时候,是不会去调用jdk中的Thread类的run方法了,而实现Runnable的方式是需要构造一个Thread了,然后传入我们实现了Runnable的这个类,当我们执行这种实现方式的时候,你会发现它会先调用jdk中的Thread类的run方法,然后才调用RunnableDemo这个类里面的run方法。这时有人可能就想说这个有什么意义呢!细心的童鞋会发现,其实我们jdk中的类也是去实现Runnable这个接口的,但是这个接口中只有一个方法,那就是run方法,而Thread类中的其他方法都是Thread自己的。我们看下Thread类的实现截图:

关于创建线程的方式有几种详解编程语言

 

其实到这里算是很明白了,我们所谓的实现Runnable,其实不过是一种实现线程运行的方式,并不是真正意义上的创建线程,这个时候我们来回味一下Oracle官网的那第一句话:There are two ways to create a new thread of execution。此时一看是不是感觉意味深长,因为你会发现他的着重点其实是execution这个词。我们上面说了实现Runnable是先执行Thread类的run方法,那我们来看下Thread类中的run方法代码如何:

    /* What will be run. */ 
    private Runnable target; 
 
    @Override 
    public void run() { 
        if (target != null) { 
            target.run(); 
        } 
    }

看上去是不是很简单,那这里的target是个什么东西呢?你点进去的话,其实它就是个Runnable的声明,但是如果你断点看过的话,或发现当在运行使用实现Runnable方式的时候,此时这个target是当前运行的RunnableDemo这个类,这是因为我们在构造Thread类的时候所传的就是RunnableDemo这个类。

这次我们对于实现创建线程的方式有几种这个答案就比较清晰明朗了,其实在面试的时候,我们可以回答:按照Oracle官网来看,是可以分为两类的,但准确来说创建线程的方式有且只有一种,那就是构建Thread类,所说的有两种方式那是实现线程单元执行的方式:

1.一类是实现Runnable接口的run方法,并把此实例传给Thread类。

2.第二种就是重写Thread的run方法(继承Thread类)

当然网上还有说三种四种的,这里面有包含Executors、Callable、FutureTask等,但如果你深究代码的话,你会发现其实Executors它的底层也是通过构造Thread来实现线程的创建的,而FutureTask的底层是实现了Runnable接口的,而网上其他的所说的无独有偶,要么是构建Thread类要么实现Runnable,所以他们并不能算真正意义上的创建线程的方式。

 

三、两种方式那是实现线程单元执行的方式的对比

前面我们说了实现线程单元执行的方式有两种,那么这两种在我们的平常开发中哪一种更实用呢?或者说更推荐那种方法呢?

前面我们把两种的特点都说了,实现Runnable接口的run方法的其最终还是调用target.run()方法,而继承Thread类是重写整个run方法。那么这两个方法哪个方法更好呢?或者说更推荐哪个方法?其实在日常开发中,推荐的还是实现Runnable接口的方式,那么继承Thread类的方式为什么不好呢?我们来做以下几点分析:

  • 从代码架构层面上看:具体的任务(run方法)应该和“创建、运行线程的机制(Thread类)”解耦,意思就是继承Thread类不具有解耦性,实现Runnable接口的就是一解耦的方式实现的。
  • 如果使用继承Thread方式,那么每次创建新的任务,只能独立新建一个线程,这样的做法消耗比较大,而Runnable便会消耗较小。
  • 继承Thread类以后,因为Java语言不支持双继承,这样就无法再继承其他的了,这样也就限制了器扩展性,而日常开发中我们还是很看重高扩展性的。

 

结论

本文从一下几个方面来进行了说明:

  • 从官网上看解答
  • 从源码层面看实现的方式,其实本质上那两种方式并没区别,都是调用start方法来新建线程。
  • 比较了两种方法的优劣

 

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/19407.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论