Java到底有哪几种创建线程的方式?(本文章目前不涉及包装类的方式, 只追本溯源)
对于这个问题, 网上的回答可谓是众说纷纭
有三种, 五种的 甚至还说有七种创建线程的方法
创建一个子线程而已, 真的分化出了这么多种方法吗?
真正的答案是: 创建线程的方法其实只有两种, 甚至本质上是同一种方法
两种创建线程的方法是哪两种?
让我们来到Oracle的官方文档
Oracle明确指出, Java语言中创建线程的方法只有两种
- 继承
Thread类, 并重写其run方法
public class ThreadCreateByExtends extends Thread {
@Override
public void run() {
System.out.println("Thread is running...");
}
public static void main(String[] args) {
ThreadCreateByExtends t1 = new ThreadCreateByExtends();
t1.start();
}
}
- 将重写了
run方法的Runnable接口实现类作为构造参数传给Thread类
public class ThreadCreateByRunnable implements Runnable{
@Override
public void run() {
System.out.println("Thread is running...");
}
public static void main(String[] args) {
ThreadCreateByRunnable task = new ThreadCreateByRunnable();
Thread t1 = new Thread(task);
t1.start();
}
}
JUC初学者看到这两段代码可能会有些许疑惑, 不急, 我们慢慢来
问题一: 什么是run方法?
不管是继承Thread类, 还是实现Runnable接口, 都需要将这个线程的职责 - 也就是你期望这个线程执行的任务写到run方法中
通俗的来说, 线程 也就是Thread其实只是执行run方法体的载体, 不同的线程之所以有区别, 本质上只是其实现的run方法任务不同
问题二: 任务下达好了, 怎么启动这个线程?
调用创建线程Thread类的start方法即可, 标志着线程的启动 注意区分start方法和run方法的区别
问题三: 这不是两种不同的方法吗, 为什么说本质上都是同一种?
回到问题一, 首先要明确一点, run方法才是被定义需要被执行的任务, 而线程(Thread)只是执行这些任务的一个实体
其中, 继承Thread创建线程通过重写父类的run方法, 将run方法(任务)和该线程类绑定在了一起, 任务和线程是紧耦合的关系
而实现Runnable接口创建线程则是将任务以Runnable实现类的形式, 作为一个"热插拔"的部件(构造方法参数)传给Thread类"槽口", 槽口和被插入的部件互不关心, 任务和线程是解耦合的关系
注意, 此处的"热插拔"指的是同一个Runnable实现类(任务)可以接入多个不同的Thread执行同一个任务,不用新建多个相同的任务, 请注意Thread被创建出来并赋予了任务之后是无法更换任务的
让我们回到问题本身
首先, 让我们先简单看看JDK8中Thread类的源码
class Thread implements Runnable {
/* What will be run. */
private Runnable target;
public Thread(Runnable target) {init(null, target, "Thread-" + nextThreadNum(), 0);}
@Override
public void run() {
if (target != null) {
target.run();
}
}
}
public interface Runnable {
public abstract void run();
}
奇怪的一点: Thread类居然也实现了Runnable接口, 并重写了其run方法?
-
不难看出, 假如是通过继承创建线程, 此处的run方法就会被重写, 原Thread中的run方法不会被执行
-
如果是实现Runnable接口创建线程, 此处的target便为构造方法传入的Runnable实现类, run方法执行的便是实现类中定义的任务
也就是说, 本质上线程(执行实体)的创建只有一种方法, 也就是通过Thread类创建
而运行内容(任务)要么来自于target(Runnable实现类),要么来自于重写的 run() 方法(继承Thread)
思考: 线程池是怎么实现传入不同任务而可以复用线程的呢?