为什么Thread类不是最终的?你为什么要延长线程?

时间:2012-05-02 14:14:45

标签: java multithreading

我把这作为面试问题。

  

为什么Thread类不是最终的?你为什么要延长线程?

我无法想出现实世界的用例。

6 个答案:

答案 0 :(得分:10)

来自Oracle's documentation

  

有两种方法可以创建新的执行线程。一种是将一个类声明为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)

两种情况:

  1. 创建一种新的Thread,也许是一种在完成后清理一些资源等等。
  2. 要覆盖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 invariantssensitive information。 但是,所有安全问题都是项目特定的,不适用于可用于非安全相关方案的工具。

便利

andersoj提到here这篇不错的IBM文章:

  

最终的课程和方法可能会给您带来很大的不便   编程 - 它们限制了重用现有代码的选项   扩展现有类的功能。虽然有时候   上课是有充分理由的,例如强制执行   不变性,使用final的好处应该超过   不方便。性能提升几乎总是一个不好的理由   妥协妥协良好的面向对象设计原则,何时   性能增强很小或不存在,这是一个坏的   实际上是权衡利弊。

因此,从我的观点来看,如果将类设置为final是没有强大的好处,如果JVM能够进行这样的性能优化,我会说方便的参数获胜。

因此,我们可以扩展Thread类并用它做任何我们想做的事情,一些初始化/完成的东西,比如日志记录,更改线程名称,线程的类型......这取决于你!

答案 5 :(得分:0)

可能是在派生的Thread类中保存线程局部变量的字段。当run()方法未被覆盖且线程像往常一样执行Runnable时,也可以访问这些变量。此类自定义主题可由标准ExecutorService管理,在自定义ThreadFactory中创建并根据需要放弃。

我知道还有ThreadLocal,但是如果字段需要清理(比如它们是数据库连接),那么通过调用super.run()来处理Runnable可以相当优雅地完成s并在run方法返回之前立即进行终结(因此线程终止)。