在此代码示例中,ExecutorService使用了一个并且允许超出范围。
public static void main(String[] args)
{
ExecutorService executorService = Executors.newFixedThreadPool(3);
executorService.submit(new Runnable()
{
public void run()
{
System.out.println("hello");
}
});
}
一旦executorService超出范围,就应该收集并最终确定。 ThreadPoolExecutor中的finalize()方法调用shutdown()。
/**
* Invokes {@code shutdown} when this executor is no longer
* referenced and it has no threads.
*/
protected void finalize() {
shutdown();
}
调用shutdown()后,池线程应终止,并允许JVM退出。但是,执行程序服务永远不会被收集,因此JVM保持活动状态。甚至对System.gc()的调用似乎也不起作用。为什么即使在main()终止后也没有收集executorService?
注意:我知道我应该自己调用shutdown(),而且我总是在测试之外做。我很好奇为什么最终确定不作为备份工作。
答案 0 :(得分:49)
这与GC不确定无关,尽管它没有帮助! (这是你的例子中的一个原因,但即使我们“修复它”以消耗内存并强制收集,它仍然无法最终确定)
执行程序创建的Worker线程是内部类,它们具有对执行程序本身的引用。 (他们需要它能够看到队列,runstate等等!)运行线程不是垃圾收集的,所以当池中的每个线程都有该引用时,它们将使执行器保持活动状态直到所有线程都死亡。如果您不手动执行某些操作来停止线程,它们将永远保持运行,您的JVM将永远不会关闭。
答案 1 :(得分:20)
Affe是正确的;线程池的线程将阻止它被垃圾收集。当您调用Executors.newFixedThreadPool(3)时,您将获得如此构造的ThreadPoolExecutor:
ThreadPoolExecutor(3, 3, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
如果您阅读了ThreadDoolExecutor的JavaDoc,它会说:
程序中不再引用的池并且没有剩余的池 线程将自动关闭。如果您想确保 即使用户忘记呼叫,也会回收未引用的池 shutdown(),然后你必须安排未使用的线程最终死掉, 通过设置适当的保持活动时间,使用零下限 核心线程和/或设置allowCoreThreadTimeOut(boolean)。
如果你想让你的线程池像你期望的那样完成,你应该做其中的一件事。
答案 2 :(得分:0)
因为垃圾收集是“非确定性的”,即您无法预测它何时会发生,因此您无法准确预测最终化方法何时运行。您只能使对象符合GC条件,并且无需担保即可向System.gc()建议gc。
更糟糕的线程是由JVM处理的操作系统特定的,并且难以预测......
答案 3 :(得分:0)
终结者太不可预测了。取决于它们通常是不好的做法。 您可以在"Effective java“中通过Joshua Bloch(项目1.7)
了解更多相关信息答案 4 :(得分:0)
一旦executorService超出范围,就应该收集并最终确定。
不是真的 - 一旦超出范围,就可以收集并最终确定。 VM spec中没有关于何时最终确定对象的保证,甚至如果最终确定的话:
Java编程语言没有指定调用终结器的时间,除非说它将在重用对象的存储之前发生。
答案 5 :(得分:0)
如果您希望在Executor服务超出范围时完成线程的确定,则应避免使用mpt,
ExecutorService executorService = Executors.newFixedThreadPool(3);`
并使用例如:
ExecutorService executorService = new ThreadPoolExecutor(0, 3, 10, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());