为什么Java ThreadPoolExecutor覆盖finalize()

时间:2014-03-21 12:05:35

标签: java multithreading garbage-collection threadpoolexecutor

我想知道为什么ThreadPoolExecutor finalize()方法在知道只有GC AFTER 所有线程调用finalize方法时才会调用其shutdown()方法已经停止了。那么为什么ThreadPoolExecutor会覆盖finalize()呢?

ThreadPoolExecutor.finalize()调用shutdown()对我来说(以及我的项目的漏洞源代码)似乎有误导性,因为这会产生强烈(但错误)的印象 - ThreadPoolExecutor管理其线程的生命周期,并在GC收集ThreadPoolExecutor对象时停止线程 - 只需要调用shutdown()或shutdownNow(),如果你想要确定性的结果,而不是依靠GC来整理(显然,做这个的做法很糟糕!)

注 在这个帖子中,why-doesnt-this-thread-pool-get-garbage-collected Affe解释了为什么客户端仍然需要调用shutdown()

在这个主题中,why-threadpoolexecutor-finalize-invokes-shutdown-and-not-shutdownnow发起者对这个主题感到困惑,但答案并不像1

那样全面。

ThreadPoolEecutor.finalize()的{​​{3}}确实包含“并且没有线程”字样,但这很容易被忽视。

1 个答案:

答案 0 :(得分:3)

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

其次,我们无法肯定地回答你的问题,因为你基本上是在问他们为什么要这样设计呢?" ......当他们做出决定时,我们并没有。

但我怀疑原因是当前的选择被认为是两个邪恶中较小的一个:

  • 一方面,如果一个线程池可能只是因为它不再被直接引用而被关闭,那么正在进行实际工作的线程可能会过早终止。

  • 另一方面,正如您所观察到的一个线程池在没有直接可达的时间内自动关闭可能是内存泄漏。

鉴于存在明显避免存储泄漏的方法,我认为第二种选择(即当前行为)是邪恶中较小的一种。

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

  

<强>最后确定

     

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

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

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

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

现在我/我们无法告诉您哪个先行,行为或javadoc规范。见我&#34;其次......&#34;点上面......

但是,就像我上面所说的那样(&#34;首先......&#34;),如果您认为这是一个错误,请将其报告给Oracle。如果您认为javadoc具有误导性,同样的情况也会发生......尽管我认为根据我引用的材料,您的论证很弱。


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

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

  • shutdown()方法通过调用一个钩子方法结束,该钩子方法显然在子类中被覆盖以执行重要的操作...根据评论。