【问题标题】:Why does Java ThreadPoolExecutor override finalize()为什么 Java ThreadPoolExecutor 会覆盖 finalize()
【发布时间】:2014-04-28 19:02:38
【问题描述】:

我想知道为什么 ThreadPoolExecutor finalize() 方法在知道 finalize 方法只被 GCAFTER 调用它的所有线程时调用它的 shutdown() 方法停了下来。那么为什么 ThreadPoolExecutor 会覆盖 finalize() 呢?

ThreadPoolExecutor.finalize() 调用 shutdown() 对我(以及我的项目的线程泄漏的来源)似乎具有误导性,因为这给人一种强烈(但错误)的印象,即 - ThreadPoolExecutor 管理其线程的生命周期,并在 GC 收集到 ThreadPoolExecutor 对象时停止线程 - 如果你想要确定的结果而不是依赖 GC 来整理,则只需要调用 shutdown() 或 shutdownNow() (显然,这样做是不好的做法!)

注意事项 在这个线程中,why-doesnt-this-thread-pool-get-garbage-collectedAffe 解释了为什么客户端仍然需要调用shutdown()

在这个帖子里,why-threadpoolexecutor-finalize-invokes-shutdown-and-not-shutdownnow发起人对这个话题感到困惑,但答案并不像1那么全面

ThreadPoolEecutor.finalize()JavaDocs 确实包含“并且它没有线程”的字样,但这很容易被忽略。

【问题讨论】:

  • 我在这里同意finalize() 的文档相当混乱......我想知道.newFixedThreadPool() 怎么会有“无线程”。
  • ThreadPoolExecutor 的 JavaDocs 有一个 Finalization 部分解释了“如果您想确保即使用户忘记调用 shutdown() 也能回收未引用的池,那么您必须安排未使用的线程最终死亡,通过设置适当的保持活动时间,使用零核心线程的下限和/或设置 allowCoreThreadTimeOut(boolean)。"。因此,线程数有可能减少并达到零。

标签: java multithreading garbage-collection threadpoolexecutor


【解决方案1】:

首先,如果您认为这是一个错误,请向 Oracle 报告。

其次,我们不能肯定地回答您的问题,因为您本质上是在问“他们为什么要这样设计”......当他们做出决定时我们并不在场。

但我怀疑原因是当前的选择被认为是两害相权取其轻:

  • 一方面,如果您的线程池可能仅仅因为它不再被直接引用而被关闭,那么正在执行实际工作的线程可能会提前终止。

  • 另一方面,正如您观察到的,如果线程池在变得更长时间直接可访问时不会自动关闭,则可能是内存泄漏。

鉴于有明显的方法可以避免存储泄漏,我认为第二种选择(即当前行为)是弊端较小的。

无论如何,有明确的证据表明设计者考虑了这种行为;即来自ThreadPoolExecutor javadoc 的引用:

完成

程序中不再引用且没有剩余线程的池将自动关闭。如果您想确保即使用户忘记调用shutdown() 也能回收未引用的池,那么您必须安排未使用的线程最终死亡,方法是设置适当的保持活动时间,使用零核心线程的下限和/或设置allowCoreThreadTimeOut(boolean)

(以及那种答案的@fge 评论。当非活动工作线程配置为超时时会发生这种情况。)

我也认为还有一个基于实现的原因。看看代码here

线程池中的每个线程都有一个Runnable的引用,它实际上是内部类ThreadPoolExecutor.Worker的一个实例。这意味着从(活动的,虽然可能是空闲的)ThreadThreadPoolExecutor.Worker 对象,以及从该对象到封闭的 ThreadPoolExecutor 实例之间存在强引用路径。由于活动线程始终是可访问的,因此 ThreadPoolExecutor 实例在其所有线程实际终止之前仍然是可访问的。

现在我/我们不能告诉你哪个先出现,行为或 javadoc 规范。见我上面的“其次...”点...

但是,就像我上面所说的(“首先...”),如果您认为这是一个错误,请将其报告给 Oracle。如果您认为 javadoc 具有误导性,情况也是如此……尽管鉴于我引用的材料,我认为您的论点很弱。


如果shutdown() 在这种情况下什么都不做,他们为什么会重载finalize() 来调用shutdown()

  • 它可能在早期版本中做了一些重要的事情,并且

  • shutdown() 方法通过调用一个在子类中显然被覆盖的钩子方法结束,以执行一些重要的事情......根据 cmets。

【讨论】:

  • 我认为实现或 JavaDocs 中没有错误。例如。线程池在有工作要做时不能停止。我的问题只是“为什么设计者选择覆盖 finalize()”,我认为你的答案是你不知道......即你不认为在 GC 的上下文中调用 shutdown()线程做任何有用的事情。
  • 正如在帖子中解释的那样,为了调用 shutdown() 而重写 finalize 的事实被高级开发人员误解为使用 ThreadPookExecutor 的优势在于它管理线程的生命周期.我无法与 Oracle 争辩说覆盖 finalize() 是一个错误,因为它们描述了“无线程”条件。无论如何,感谢您抽出时间来依赖。
  • @Diarmuid - 我能说什么?也许高级开发人员只需要更仔细地阅读 javadocs ......尤其是如果 >>you将是一个错误。并且说 javadoc 具有误导性并且不向 Oracle 报告是一种逃避。)
猜你喜欢
  • 1970-01-01
  • 2012-07-16
  • 1970-01-01
  • 2011-02-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-08
相关资源
最近更新 更多