为什么 Thread 实现 Runnable?

2022-09-01 07:49:18

当线程启动时,Java 线程的 run() 方法由该线程上的 JVM 调用。为了给线程一些事情做,你可以创建一个 Thread 的子类并重写它的 run() 方法,或者(首选)你可以为线程的构造函数提供一个 Runnable。没关系。

我正在制作Thread的子类并重写运行,我意识到我无法像预期的那样保护该方法,因为Thread.run()是公开的。然后我意识到了原因:它必须是公开的,因为Thread实现了Runnable。但是它为什么要实现Runnable呢?

这似乎不合逻辑。线程是可启动的(从当前线程),但您运行它的方式与运行()Runnable(从当前线程)的方式不同;线程自行运行(在其自己的线程上)。如果您确实手动调用了Thread的run方法,那么您就不会将其用作Thread,而只是一个重量级的Runnable。

由于设计的原因,任何有权访问 Thread 对象的代码都可以调用其公共运行方法,并可能戳入不打算公开或设计为以这种方式调用的代码。它还允许像这样非常奇特的事情:

Thread.currentThread.run();

线程实现 Runnable 是否有我没有看到的合法用途?


答案 1

原因是“向后兼容性”。

该类起源于Java 1.0 ...或更早。在那些日子里,Java没有内部类,所以没有一种轻量级的方法来实现一个实例。如果您查看那个时代的旧线程示例和教程,通常会看到扩展和重写该方法的类。ThreadRunnableThreadrun()

随着时间的推移,人们意识到扩展不是一个好主意(出于各种原因)。但是,设计无法更改,因为这会使旧的Java代码与较新的JVM不兼容。ThreadThread


线程实现 Runnable 是否有我没有看到的合法用途?

这取决于你所说的“合法”是什么意思。

  • 早期编写的旧代码并不是因为以旧的方式做事而“非法”。它没有任何“破碎”的东西。

  • 在某些情况下扩展 Thread 并重写该方法确实有意义。例如,您可能希望实现一些特殊的机制来传入或传出所提供的信息,或者实现一些特殊的异常处理,或者...使线程“可重新启动”。run()run()Runnable

  • 甚至可能存在您希望直接在线程对象上调用的情况。例如,如果您收到一些扩展的“狗早餐”代码,并且您必须将其转换为在线程池中运行而无需修改原始代码。您可以考虑实例化 crufty 线程类,并将实例作为可运行实例传递给要运行的线程池。(是的...太可怕了!run()Thread


答案 2

覆盖运行并不能解释为什么它需要公开。

如果这是你的问题,我认为有简单的答案:实现接口的方法必须始终在java中是公开的。使用抽象类可以保护它,但是如果Thread是抽象的,您将无法将其用作抽象类。

至于为什么Thread首先实现Runnable,java必须有一种方法来知道线程实际上在哪个部分完成它的工作。他们为此使用运行方法。他们本可以更清楚地将实现Runnable和Thread子类实现的逻辑分开,但这是我认为由于历史原因而难以改变的小错误。

TLDR;AFAIK确实没有一个很好的理由让Thread实现Runnable,但是它实现某种接口是有原因的 - 他们应该只是有某种分离的接口,如ThreadRunnable,而不是使用相同的Runnable接口,也有其他用途。

还要注意的是,在现代Java上,你可能应该使用Callables和FutureTasks而不是Threads。“超时的集成,适当的取消和现代并发支持的线程池对我来说都比成堆的原始线程更有用。


推荐