为什么Thread实现Runnable?

时间:2013-08-19 03:21:34

标签: java runnable

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

我正在制作一个Thread的子类并重写run,我意识到我无法按照我的预期保护该方法,因为Thread.run()是公共的。然后我意识到了原因:它必须是公共的,因为Thread实现了Runnable。但为什么它实现了Runnable?

这似乎不符合逻辑。一个线程是 startable (来自当前线程),但是你没有像运行()一个Runnable(来自当前线程)那样运行它;线程自己运行(在自己的线程上)。如果您手动调用Thread的run方法,那么您不会将它用作Thread,只是一个重量级的Runnable。

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

Thread.currentThread.run();

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

2 个答案:

答案 0 :(得分:24)

原因是“向后兼容”。

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

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


  

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

这取决于你所说的“合法”。

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

  • 潜在的场景,扩展线程并覆盖run()方法是有意义的。例如,您可能希望run()实现一些特殊的机制,用于将信息传入或传出提供的Runnable,或实现一些特殊的异常处理,或者......使线程“可重启”。 / p>

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

答案 1 :(得分:4)

  

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

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

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

TLDR; AFAIK确实没有一个很好的理由让Thread实现Runnable,但是它有理由实现某种类型的接口 - 它们应该只是有一些像ThreadRunnable这样的独立接口而不是使用它Runnable接口也有其他用途。

另请注意,在现代java上,你应该使用Callables和FutureTasks而不是Threads。 “超时,正确取消和现代并发支持的线程池的集成对我来说比成堆的原始线程更有用。”,在堆栈溢出中引用another answer