我对使用ExectuorService
计划的线程有序关闭的想法很满意;也就是说,调用shutdown
或shutdownNow
将导致池上创建的线程正常退出。如果他们回复interrupt
,你可以确定最终会被调用等等,你会得到一个干净,可预测的退出(你可以清理任何资源等)。
但是,如果您已将线程设置为守护程序(通过执行程序的ThreadFactory
),如下所示。
ExecutorService pool = Executors.newSingleThreadExecutor(new ThreadFactory() {
@Override
public Thread newThread(Runnable runnable) {
Thread thread = Executors.defaultThreadFactory().newThread(runnable);
thread.setDaemon(true);
return thread;
}
});
主线程终止后,VM将突然终止任何守护程序线程。 在上面的示例中,计划然后突然终止的(守护程序)线程将绕过任何finally块,并且任何可中断的方法都不会抛出InterruptedException
。
所以,我倾向于认为将ThreadPoolExecutor
池中使用的线程标记为守护进程是不好的做法......我的问题实际上是帮助我发声为什么。 / p>
为什么在ExecutorService
的线程池中使用守护程序线程是不好的练习(或者如果你不同意的话)?特别是我有兴趣通过正常关闭(具有中断策略并且运行良好的线程)与守护程序线程来描述VM关闭的生命周期。
扩展最后一点,finalize
上的ThreadPoolExecutor
会自行调用shutdown
,但是当它使用守护程序线程时,如果finalize
被调用,它们可能已经终止VM。那么线程池的行为是什么呢?如果底层线程突然终止,它是否可以被欺骗以保持活着(因此不会退出VM)?
我问的部分原因是因为我已经看到它曾经绕过了关闭实际的ExectorService的需要。你能想到绕过其关闭生命周期会产生不良影响的情况吗?到目前为止,我能够提出使用守护进程的唯一原因是采取捷径,我希望能够理解它可能造成的任何意外的副作用。
答案 0 :(得分:14)
在ExecutorService的线程池中使用守护程序线程是不好的做法吗?
如果发送到特定ExecutorService
的任务可以突然终止,那么为什么不是,这是守护程序线程的作用。但通常情况下,没有很多任务可以在没有关闭仪式的情况下终止,因此如果您选择守护程序线程,您必须知道自己在做什么。
finalize()
。无法保证任何特定对象何时(如果有的话)都是GCd,并且ThreadPoolExecutor
也不例外,因此可以调用或不调用finalize()
。行为取决于特定的JRE实现,即使使用相同的实现,也可能会不时变化。
答案 1 :(得分:5)
我倾向于为守护程序和非守护程序线程使用不同的池。守护进程池往往会执行重复清理作业,监视和后台任务,如果一个或两个未执行则无关紧要。任何只在应用程序仍在运行时才有意义的任务都可以创建一个守护程序线程任务。例如GC线程是守护程序线程。
答案 2 :(得分:5)
守护程序线程可能很有用,如果它们没有突然终止,它们就不会那么有用IMO。
据推测,我们可以想象另一种类型的线程,当没有正常线程运行时它会被中断,而不是突然终止。这可能有点方便,但如果你不得不做任何清理工作,那么你很可能想要有条不紊地进行清理。这将限制此功能的便利性。
另一方面,如果你有任务在关机时不需要任何清理,那么deamon线程非常方便。并且你不想浪费时间等待它们到达某个特定状态或冒着停机等问题,因为你使用deamon线程的原因是因为你不需要任何清理。如果应用程序执行任何操作将是浪费时间。正在关闭。如果你关心,那么你不应该使用deamon线程。
与deamon线程池没什么不同。如果该线程池正在执行在关闭时不需要进行任何清理的任务,那么由于方便起见,这将是有意义的。
来自JCiP book:
应该谨慎使用守护程序线程,可以在没有清理的情况下随时安全地放弃处理活动。特别是,将守护程序线程用于可能执行任何类型I / O的任务是危险的。守护程序线程最好保存用于“内务”任务,例如后台线程定期从内存缓存中删除过期的条目