直接继承 Thread 类和实现 Runnable 接口来创建线程有和异同

在 Java 中,常用的创建线程方式有两种,一种是继承 Thread 类;另一种是直接实现 Runnable 接口,然后 @Override 其中的 run 方法。今天我们就来探究一下这两种方式的异同点吧。

Runnable 和 Thread 的关系

说穿了,Thread 类就是 Java 官方给我们提供的一种 Runnable 接口的实现。

Runnable 是一个接口

Runnable 源码

1
2
3
4
public interface Runnable {
// Runnable 接口中只定义了一个 run 抽象方法。
public abstract void run();
}

Thread 是一个实现了 Runnable 接口的类

Thread 部分源码

1
2
3
4
5
6
7
8
9
public class Thread implements Runnable {
// 看 Thread 类定义可以很清楚的看到它是实现了 Runnable 接口的,而且也 @Override 了 run 方法。
@Override
public void run() {
if (target != null) {
target.run();
}
}
}

所以这两种创建线程的方式的异同就变成了继承类和实现接口之间的区别。一般来说,当 Java 开发中遇到多线程的情况,建议以实现 Runnable 接口为主,因为实现 Runnable 接口相比继承 Thread 类有如下优势:

  • 可以避免由于 Java 的单继承特性而带来的局限;
  • 适合多个线程去处理同一资源的情况;

举个例子

下面以经典的卖票程序为例,来说明二者的区别。

继承 Thread 类实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.thread;

/**
* 继承 Thread 类
*
*/
public class ThreadDemo extends Thread {
private int ticket = 10;
private String name;

public ThreadDemo(String name) {
this.name = name;
}

@Override
public void run() {
for (int i = 0; i < 10; i++) {
if (this.ticket > 0) {
System.out.println(name + " 剩余票数:" + ticket--);
}
}
}
}

测试程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.thread;

/**
* 探究继承 Thread 类和实现 Runnable 接口这两种方法有什么不同的地方。
*
*/
public class MyThread {

public static void main(String[] args) {
ThreadDemo t1 = new ThreadDemo("ThreadDemo 一号窗口");
ThreadDemo t2 = new ThreadDemo("ThreadDemo 二号窗口");
ThreadDemo t3 = new ThreadDemo("ThreadDemo 三号窗口");
t1.start();
t2.start();
t3.start();

// RunnableDemo rd = new RunnableDemo();
// Thread t1 = new Thread(rd, "RunnableDemo 一号窗口");
// Thread t2 = new Thread(rd, "RunnableDemo 二号窗口");
// Thread t3 = new Thread(rd, "RunnableDemo 三号窗口");
// t1.start();
// t2.start();
// t3.start();

}

}


运行结果

很明显,这里虽然启用了三个线程,但三个线程各自完成了 10 次的卖票任务,并没有发挥多线程处理任务速度快的优势。下面我们看看 Runnable 接口实现。

实现 Runnable 接口实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.thread;

/**
* 实现 Runnable 接口
*
*/
public class RunnableDemo implements Runnable {
private int ticket = 10;

@Override
public void run() {
for (int i = 0; i < 10; i++) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + " 剩余票数:" + ticket--);
}
}
}
}

测试程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.thread;

/**
* 探究继承 Thread 类和实现 Runnable 接口这两种方法有什么不同的地方。
*
*/
public class MyThread {

public static void main(String[] args) {
// ThreadDemo t1 = new ThreadDemo("ThreadDemo 一号窗口");
// ThreadDemo t2 = new ThreadDemo("ThreadDemo 二号窗口");
// ThreadDemo t3 = new ThreadDemo("ThreadDemo 三号窗口");
// t1.start();
// t2.start();
// t3.start();

RunnableDemo rd = new RunnableDemo();
Thread t1 = new Thread(rd, "RunnableDemo 一号窗口");
Thread t2 = new Thread(rd, "RunnableDemo 二号窗口");
Thread t3 = new Thread(rd, "RunnableDemo 三号窗口");
t1.start();
t2.start();
t3.start();

}

}


运行结果

如此,便发挥出了多线程的优势。

引用