34

当线程启动时,JVM 在该线程上调用Java 线程的run()方法。要让线程有事可做,您可以创建 Thread 的子类并覆盖其 run() 方法,或者(首选)您可以为线程的构造函数提供 Runnable。没关系。

我正在创建 Thread 的子类并覆盖运行,但我意识到我无法像预期的那样使方法受到保护,因为 Thread.run() 是公共的。然后我意识到为什么:它必须是公开的,因为 Thread 实现了 Runnable。但是它为什么要实现 Runnable 呢?

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

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

Thread.currentThread.run();

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

4

2 回答 2

27

原因是“向后兼容性”。

该类Thread起源于 Java 1.0 ... 或更早版本。在那个时候,Java 没有内部类,因此没有一种轻量级的方法来实现Runnable实例。如果您查看那个时代的旧线程示例和教程,通常会看到扩展Thread和覆盖该run()方法的类。

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


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

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

  • 早期编写的旧代码并不是因为按旧方式做事而“非法”。没有什么“破碎”的。

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

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

于 2013-08-19T03:30:17.597 回答
4

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

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

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

TLDR;AFAIK确实没有一个很好的理由让一个线程实现Runnable,但是它有理由实现某种类似的接口——他们应该只是可能有某种分离的接口,比如ThreadRunnable,而不是使用相同的接口可运行的接口也有其他用途。

另请注意,在现代 Java 上,您可能无论如何都应该使用 Callables 和 FutureTasks 而不是 Threads。“超时的集成、适当的取消和现代并发支持的线程池对我来说都比成堆的原始线程有用得多。”,在 stackoverflow 上引用另一个答案。

于 2013-08-19T06:09:30.843 回答