【问题标题】:why isn't Thread class final? Why would you extend a Thread ever?为什么线程类不是最终的?你为什么要扩展一个线程呢?
【发布时间】:2012-05-02 14:14:44
【问题描述】:

这是面试题。

为什么 Thread 类不是 final 的?你为什么要扩展一个线程?

我想不出真实世界的用例。

【问题讨论】:

标签: java multithreading


【解决方案1】:

来自Oracle's documentation:

有两种方法可以创建一个新的执行线程。一种是将类声明为 Thread 的子类。这个子类应该重写类 Thread 的 run 方法。创建线程的另一种方法是声明一个实现 Runnable 接口的类。

所以答案是“您可能希望继承 Thread 以覆盖其 run() 方法。”

引用的段落可以追溯到 JDK 1.1 的 Java 文档中。 Java 添加了其他方便的类来管理并发,最值得注意的是 cmets 中提到的执行器,可能会减少或消除扩展 Thread 的需要。但是,他们无法做到final,因为这会破坏向后兼容性。

就实际原因而言,我认为您今天可能想要扩展Thread 而不是实现Runnable 的唯一原因是重写它的方法而不是 em>run()。例如,您可能想要添加日志记录或额外的清理。

【讨论】:

  • 我发现如果线程正在执行 IO(即关闭套接字以中断读/写),覆盖 interrupt() 也很有用。
【解决方案2】:

这几乎是从 John Vint 的评论中摘录的,但我认为这是最好的答案。

唯一一次我能想到我可能在哪里扩展Thread而不是实现Runnable——或者,更好的是,只使用ExecutorService和Future——是我需要覆盖Thread.interrupt() 进行一些清理的时候。否则,我看不出任何实际扩展 Thread 的实际理由。

【讨论】:

    【解决方案3】:

    两种情况:

    1. 创建一种新的Thread,也许是在完成后清理一些资源等
    2. 重写run() 方法,而不是向构造函数提供Runnable(注意:避免这种模式 - 这不是正确的方法)

    【讨论】:

      【解决方案4】:

      Thread 不是final 的另一个原因是在Java 的早期,覆盖run() 被认为是一种好的设计模式。 (我想,在匿名类之前的日子里,人们认为子类Thread 比创建一个实现Runnable 的独立类更“整洁”。)

      无论如何,一旦 Java 1.0 发布,因为不可能通过将 Thread 更改为最终版本来解决问题。那会破坏很多现有的代码。

      【讨论】:

        【解决方案5】:

        让我这样说:在设计一门语言时,就像其他任何工具一样,我们应该考虑利弊,而不是仅仅限制工具,因为我们无法看到适用的真实用例。通过这样做,我们能够从理论上理解这些决定。基本上,练习是反过来问。

        为什么线程应该是最终的?

        让我进入正题。


        (我不包括向后兼容性参数,我个人不喜欢它,因为我认为我们应该始终专注于本地改进)

        为了能够讨论这样的主题,我们需要了解将类声明为 final 与否的好处。

        性能

        HereTofuBeer 说:

        虚拟(覆盖)方法通常通过某种方式实现 最终是一个函数指针的表(vtable)。每种方法 call 具有必须通过该指针的开销。什么时候 类被标记为final,那么所有的方法都不能被覆盖 并且不再需要使用表格 - 这更快。

        某些虚拟机(例如 HotSpot)可能会更智能地做事并知道何时 方法被/未被覆盖并生成更快的代码 合适。

        安全

        Hereoracle 亮点:

        最好在设计 API 时考虑到安全性。试图改造 现有 API 的安全性更加困难且容易出错。为了 例如,将类设为 final 可防止恶意子类 添加终结器、克隆和覆盖随机方法。

        还有issues with invariants 和sensitive information。 但是,所有安全问题都是项目特定的,不适用于可用于非安全问题场景的工具。

        方便

        andersoj 推荐了 here 这篇不错的 IBM 文章:

        final 类和方法在以下情况下可能会带来很大的不便 编程——它们限制了您重用现有代码的选择,并且 扩展现有类的功能。虽然有时一个 类被定为 final 是有充分理由的,例如强制执行 不变性,使用 final 的好处应该超过 不便。性能提升几乎总是一个不好的理由 妥协良好的面向对象设计原则,当 性能提升很小或不存在,这是一个坏的 确实是取舍。

        因此,从我的角度来看,如果将类设置为 final 并没有很大的好处,如果 JVM 能够进行此类性能优化,我会说方便的论点获胜。

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

        【讨论】:

          【解决方案6】:

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

          我知道还有ThreadLocal,但如果字段需要清理(假设它们是数据库连接),这可以通过调用super.run() 来处理Runnables 并在之前立即完成run 方法返回(因此线程终止)。

          【讨论】:

            猜你喜欢
            • 2013-08-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-07-24
            • 1970-01-01
            • 2018-11-06
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多