我把这作为面试问题。
为什么Thread类不是最终的?你为什么要延长线程?
我无法想出现实世界的用例。
答案 0 :(得分:10)
有两种方法可以创建新的执行线程。一种是将一个类声明为Thread的子类。该子类应该重写Thread类的run方法。创建线程的另一种方法是声明一个实现Runnable接口的类。
所以答案是“您可能希望将Thread
子类化为覆盖其run()
方法。”
引用的段落已经在Java文档中回溯到JDK 1.1。 Java已经添加了其他方便的类来管理并发,最值得注意的是,注释中提到的执行程序,可能会减少或消除扩展Thread
的需要。然而,它们无法使它成为final
,因为这会破坏向后兼容性。
就实际原因而言,我认为您今天可能希望扩展Thread
而不是实现Runnable
的唯一原因是覆盖其方法 run()
以外的其他人。例如,您可能希望添加日志记录或其他清理。
答案 1 :(得分:2)
这几乎只取自John Vint的评论,但我认为这是最好的答案。
我唯一能想到可能扩展Thread
而非实施Runnable
的地方 - 或者更好的是,只需使用ExecutorService
一个Future
- 当我需要覆盖Thread.interrupt()
进行一些清理时。否则,我看不出实际扩展Thread
的任何实际原因。
答案 2 :(得分:1)
两种情况:
Thread
,也许是一种在完成后清理一些资源等等。run()
方法,而不是向构造函数提供Runnable
(注意:避免使用此模式 - 它不是正确的方法)答案 3 :(得分:1)
Thread
不是最终的另一个原因是,在Java的早期,覆盖run()
被认为是一种很好的设计模式。 (我想,在匿名课程之前的几天,它被认为是“更整洁”到子类Thread
,而不是创建一个实现Runnable
的独立类。)
无论如何,一旦Java 1.0发布,它就不可能通过将Thread
更改为最终来解决问题。这会打破很多现有的代码。
答案 4 :(得分:1)
让我用以下方式说明这一点:在设计一种与任何其他工具一样的工具时,应该考虑利弊,而不仅仅是限制工具,因为我们无法看到适用的实际用例。通过这样做,我们能够理论上理解这些决定。基本上,这个练习反过来问。
为什么Thread应该是最终的?
让我谈谈这一点。
(我不包括向后兼容性论点,个人而言,我不喜欢它,因为我认为我们应该始终专注于改善本地)
为了能够讨论这样的主题,我们需要了解宣布课程是否为最终的好处。
Here TofuBeer 说:
虚拟(重写)方法通常通过某种方式实现 table(vtable)最终是一个函数指针。每种方法 调用具有必须通过该指针的开销。什么时候 类被标记为final,然后所有方法都无法被覆盖 并且不再需要使用表 - 这样更快。
某些虚拟机(如HotSpot)可能会更智能地执行操作并知道何时执行操作 方法被覆盖并生成更快的代码 合适的。
Here oracle亮点:
设计具有安全性的API更好。试图改造 现有API的安全性更加困难且容易出错。对于 例如,使类最终防止恶意子类 添加终结器,克隆和覆盖随机方法。
还有issues with invariants和sensitive information。 但是,所有安全问题都是项目特定的,不适用于可用于非安全相关方案的工具。
andersoj提到here这篇不错的IBM文章:
最终的课程和方法可能会给您带来很大的不便 编程 - 它们限制了重用现有代码的选项 扩展现有类的功能。虽然有时候 上课是有充分理由的,例如强制执行 不变性,使用final的好处应该超过 不方便。性能提升几乎总是一个不好的理由 妥协妥协良好的面向对象设计原则,何时 性能增强很小或不存在,这是一个坏的 实际上是权衡利弊。
因此,从我的观点来看,如果将类设置为final是没有强大的好处,如果JVM能够进行这样的性能优化,我会说方便的参数获胜。
因此,我们可以扩展Thread类并用它做任何我们想做的事情,一些初始化/完成的东西,比如日志记录,更改线程名称,线程的类型......这取决于你!
答案 5 :(得分:0)
可能是在派生的Thread
类中保存线程局部变量的字段。当run()
方法未被覆盖且线程像往常一样执行Runnable
时,也可以访问这些变量。此类自定义主题可由标准ExecutorService
管理,在自定义ThreadFactory
中创建并根据需要放弃。
我知道还有ThreadLocal
,但是如果字段需要清理(比如它们是数据库连接),那么通过调用super.run()
来处理Runnable
可以相当优雅地完成s并在run
方法返回之前立即进行终结(因此线程终止)。