Java 线程的 run() 方法。要让线程有事可做,您可以创建 Thread 的子类并重写其 run() 方法,或者(首选)您可以向线程的构造函数提供 Runnable。没关系。
我正在创建 Thread 的子类并重写 run,我意识到我无法像我预期的那样使该方法受到保护,因为 Thread.run() 是公共的。然后我意识到为什么:它必须是公共的,因为 Thread 实现了 Runnable。但为什么它要实现Runnable呢?
这似乎不合逻辑。线程是可启动的(来自当前线程),但运行它的方式与 run() Runnable(来自当前线程)的方式不同;线程自行运行(在自己的线程上)。如果您手动调用 Thread 的 run 方法,那么您就不是将其用作 Thread,而只是将其用作重量级 Runnable。
由于设计原因,任何有权访问 Thread 对象的代码都可以调用其公共 run 方法,并可能会插入不打算公开或设计为以这种方式调用的代码。它还允许像这样非常奇特的事情:
Thread.currentThread.run();
线程实现 Runnable 是否有我没有看到的合法用途?
原因是“向后兼容”。
Thread
类起源于Java 1.0 ...或更早版本。 那时,Java 没有内部类,因此没有轻量级的方法来实现 Runnable
实例。 如果您查看那个时代的旧线程示例和教程,通常会看到扩展 Thread
并重写 run()
方法的类。
随着时间的推移,人们意识到延长
Thread
并不是一个好主意(出于各种原因)。 但是,Thread
的设计无法更改,因为这会使旧的 Java 代码与新的 JVM 不兼容。
线程实现 Runnable 是否有我没有看到的合法用途?
这取决于你所说的“合法”是什么意思。
早期编写的旧代码并不是因为以旧方式做事而“非法”。 它没有任何“破损”。
在某些潜在场景中,扩展 Thread 并重写
run()
方法确实有意义。 例如,您可能希望 run()
实现一些特殊机制,用于将信息传入或传出所提供的 Runnable
,或者实现一些特殊的异常处理,或者......使线程“可重新启动”。甚至可能在某些情况下您想直接在线程对象上调用
run()
。 例如,如果您收到一些扩展了 Thread
的“狗早餐”代码,并且您必须将其转换为在线程池中运行,而无需修改原始代码。 您可能会考虑实例化 crufty 线程类并将实例作为可运行对象传递到线程池来运行。 (是的...太可怕了!)
如果这是您的问题,我认为有一个简单的答案:实现接口的方法在 java 中必须始终是公共的。可以使用抽象类来保护它,但如果 Thread 是抽象的,您将无法这样使用它。
至于为什么Thread要实现Runnable,java必须有一种方法来知道线程实际上在哪个部分做它的工作。他们为此使用 run 方法。他们可以更清楚地分离仅实现 Runnable 和让 Thread 子类实现它的逻辑,但这是我认为的一个小错误,由于历史原因很难改变。
TLDR; AFAIK Thread 确实没有充分的理由实现 Runnable,但是它有理由实现某种类似的接口 - 他们应该只是有某种像 ThreadRunnable 这样的独立接口,而不是使用相同的接口可运行的接口还有其他用途。
还要注意,在现代 Java 上,您可能应该使用 Callables 和 FutureTasks 而不是线程。 “对我来说,集成超时、正确取消和现代并发支持的线程池比一堆原始线程有用得多。”,引用 stackoverflow 上的
另一个答案我认为这些问题的答案是:“当我们通过扩展Thread类来定义线程时,我们不能扩展任何其他类。因此,我们缺少继承。 但是如果我们通过实现 runnable 接口来定义线程,我们也可以扩展任何其他类。因此我们不会错过任何继承福利。”