Java 实现 Runnable 与继承 Thread 的对比

更新于 2025-12-29

baeldung 2024-01-08

1. 引言

“我应该实现 Runnable 还是继承 Thread 类?”这是一个非常常见的问题。

在本文中,我们将探讨在实践中哪种方式更为合理,以及背后的原因。

2. 使用 Thread

首先,我们定义一个继承自 ThreadSimpleThread 类:

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 更优的选择。