“我应该实现Runnable还是扩展Thread类?”这是一个很常见的问题。
在本文中,我们将看到哪种方法在实践中更有意义以及为什么。
使用 Thread
我们首先定义一个扩展线程Thread的SimpleThread类:
public class SimpleThread extends Thread {
private String message;
// standard logger, constructor
@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();
}
在单独的线程中运行单个日志操作需要大量代码。
此外,请注意 SimpleThread 不能扩展任何其他类,因为 Java 不支持 多重继承 。
实现Runnable
现在,让我们创建一个实现 java.lang .Runnable 接口:
class SimpleRunnable implements Runnable {
private String message;
// standard logger, constructor
@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();
}
由于我们现在正在实现一个接口,如果需要,我们可以自由扩展另一个基类。
从Java 8开始,任何公开单个抽象方法的接口都被视为函数接口,这使其成为有效的 lambda表达式 目标。
我们可以使用lambda表达式重写上述可运行代码:
@Test
public void givenARunnableLambda_whenSubmitToES_thenResult()
throws Exception {
executorService.submit(
() -> log.info("Lambda runnable executed!"));
}
Runnable还是Thread?
简单地说,我们通常鼓励使用 Runnable :
- 在扩展 Thread 类时,我们没有重写它的任何方法。相反,我们重写了 Runnable 方法(线程恰好实现了该方法)。这明显违反了 IS-A 线程原则
- 创建 Runnable 的实现并将其传递给 Thread 类利用组合而不是继承,这更灵活
- 在扩展 Thread 类之后,我们不能扩展任何其他类
- 从Java 8开始, Runnables 可以表示为lambda表达式
结论
实现Runnable通常比扩展Thread类更好。