0%

究竟如何创建新线程

Java多线程的实现方式到底有几种?网上和书籍中的说法鱼龙混杂,其实多线程的实现方式只有两种,分别是继承Thread类与实现Runnable接口。oracle官方文档中Thread类也明确了实现多线程的方式只有两种。

实现Runnable接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

/**
* @author gelong
* @date 2020/5/30 0:30
*/
public class RunnableStyle implements Runnable {
@Override
public void run() {
System.out.println("实现Runnable接口");
}

public static void main(String[] args) {
Thread thread = new Thread(new RunnableStyle());
thread.start();
}
}

继承Thread类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

/**
* @author gelong
* @date 2020/5/30 0:36
*/
public class ThreadStyle extends Thread {
@Override
public void run() {
System.out.println("继承Thread类");
}

public static void main(String[] args) {
ThreadStyle thread = new ThreadStyle();
thread.start();
}
}

两种方法的对比

Runnable相比Thread存在明显的优点:

  • 从代码架构角度:具体的任务(run方法)应该和“创建和运行线程的机制(Thread类)”解耦,用runnable对象可以实现解耦。
  • 使用继承Thread的方式的话,那么每次想新建一个任务,只能新建一个独立的线程,而这样做的损耗会比较大(比如重头开始创建一个线程、执行完毕以后再销毁等。如果线程的实际工作内容,也就是run()函数里只是简单的打印一行文字的话,那么可能线程的实际工作内容还不如损耗来的大)。如果使用Runnable和线程池,就可以大大减小这样的损耗。
  • 继承Thread类以后,由于Java语言不支持双继承,这样就无法再继承其他的类,限制了可扩展性。

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

1
2
3
4
5
6
7

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

从run()方法的实现我们可以看出:实现Runnable接口最终调用target.run(),而继承Thread类run()方法整个都被重写。但是我们同时使用这两种方式会怎么样?代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

/**
* @author gelong
* @date 2020/5/30 1:07
*/
public class BothRunnableThread {

public static void main(String[] args) {
new Thread(() -> {
System.out.println("我来自Runnable");
}) {

@Override
public void run() {
System.out.println("我来自Thread");
}
}.start();
}
}

结果只会打印”我来自Thread”,因为Thread类中的run()方法已经被重写了,所以不会执行Runnable类的run()方法了。

总结

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