baeldung 2024-01-08
1. 引言
“我应该实现 Runnable 还是继承 Thread 类?”这是一个非常常见的问题。
在本文中,我们将探讨在实践中哪种方式更为合理,以及背后的原因。
2. 使用 Thread
首先,我们定义一个继承自 Thread 的 SimpleThread 类:
public class SimpleThread extends Thread {
private String message;
// 标准的日志记录器和构造函数
@Override
public void run() {
log.info(message);
}
}
接下来,我们看看如何运行这种类型的线程:
@Test
public void givenAThread_whenRunIt_thenResult()
throws Exception {
Thread thread = new SimpleThread(
"SimpleThread executed using Thread");
thread.start();
thread.join();
}
我们也可以使用 ExecutorService 来执行该线程:
@Test
public void givenAThread_whenSubmitToES_thenResult()
throws Exception {
executorService.submit(new SimpleThread(
"SimpleThread executed using ExecutorService")).get();
}
为了在一个独立线程中执行一个简单的日志操作,上面的代码显得有些冗长。
此外,请注意:由于 Java 不支持多重继承,SimpleThread 无法再继承其他类。
3. 实现 Runnable
现在,我们创建一个实现 java.lang.Runnable 接口的简单任务:
class SimpleRunnable implements Runnable {
private String message;
// 标准的日志记录器和构造函数
@Override
public void run() {
log.info(message);
}
}
上述 SimpleRunnable 本质上只是一个我们希望在独立线程中执行的任务。
我们可以采用多种方式来运行它。其中一种方式是使用 Thread 类:
@Test
public void givenRunnable_whenRunIt_thenResult()
throws Exception {
Thread thread = new Thread(new SimpleRunnable(
"SimpleRunnable executed using Thread"));
thread.start();
thread.join();
}
我们甚至可以使用 ExecutorService:
@Test
public void givenARunnable_whenSubmitToES_thenResult()
throws Exception {
executorService.submit(new SimpleRunnable(
"SimpleRunnable executed using ExecutorService")).get();
}
关于 ExecutorService 的更多内容,可参阅相关资料。
由于我们现在实现的是一个接口,因此如果需要,我们仍然可以自由地继承另一个基类。
从 Java 8 开始,任何只暴露一个抽象方法的接口都被视为函数式接口,从而可以作为 lambda 表达式的目标。
我们可以使用 lambda 表达式重写上面的 Runnable 代码:
@Test
public void givenARunnableLambda_whenSubmitToES_thenResult()
throws Exception {
executorService.submit(
() -> log.info("Lambda runnable executed!"));
}
4. Runnable 还是 Thread?
简而言之,我们通常更推荐使用 Runnable 而非继承 Thread:
- 当我们继承
Thread类时,并没有真正重写Thread自身的方法,而是重写了Runnable接口中的run()方法(而Thread恰好实现了该接口)。这明显违背了“IS-A Thread”(是一个线程)的设计原则。 - 创建
Runnable的实现并将其传递给Thread类,利用的是组合而非继承,这种方式更加灵活。 - 一旦继承了
Thread类,我们就无法再继承其他类。 - 从 Java 8 起,
Runnable可以用 lambda 表达式简洁地表示。
5. 结论
在本篇简短教程中,我们探讨了为何通常实现 Runnable 是比继承 Thread 更优的选择。