调度在 ExecutorService 上的守护线程;解释为什么这是不好的形式
我对使用 ExectuorService 调度的线程有序关闭的想法感到满意;也就是说,调用 shutdown
或 shutdownNow
将导致池中创建的线程正常退出。如果它们响应中断
,您可以确定最终等将被调用,并且您将得到一个干净的、可预测的退出(您可以在其中清理任何资源等)。
但是,如果您已将线程设置为守护进程(通过执行程序的 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 池中使用的线程标记为守护进程是不好的做法......我的问题实际上是帮助我说出为什么。
为什么在 ExecutorService 的线程池中使用守护线程是不好的做法(如果您不同意,则不是)?我特别感兴趣的是描述虚拟机关闭的生命周期,正常关闭(具有中断策略并且运行良好的线程)与守护线程。
扩展最后一点,finalize
ThreadPoolExecutor 上的 shutdown
会自行调用 shutdown
,但当它使用守护线程时,如果 VM 调用 finalize
,它们可能已经终止。那么线程池的行为是什么呢?如果底层线程突然终止,它是否会被欺骗以保持活动状态(因此不退出虚拟机)?
我问的部分原因是因为我看到它用来绕过关闭实际 ExectorService 的需要。您能想到绕过其关闭生命周期可能产生不良影响的场景吗?到目前为止,我能想到的使用守护进程的唯一原因是走捷径,我想了解它可能导致的任何意外副作用。
I'm comfortable with the idea of orderly shutdown on threads scheduled with an ExectuorService
; that is to say, calling shutdown
or shutdownNow
will cause threads created on the pool to exit gracefully. If they respond to interrupt
you can be sure finally etc will be called and you'll get a clean, predictable exit (where you can cleanup any resources etc).
However, if you've set your thread to be a daemon (via the executor's ThreadFactory
) as below.
ExecutorService pool = Executors.newSingleThreadExecutor(new ThreadFactory() {
@Override
public Thread newThread(Runnable runnable) {
Thread thread = Executors.defaultThreadFactory().newThread(runnable);
thread.setDaemon(true);
return thread;
}
});
after the main thread terminates, the VM will abruptly terminate any daemon threads. In the example above, a (daemon) thread scheduled and then abruptly terminated will bypass any finally blocks and any interruptable methods won't throw InterruptedException
.
So, I tend to think that marking the threads used in a ThreadPoolExecutor
's pool as daemon is bad practice... my question is really about helping me vocalise why.
Why is it bad practice (or not if you disagree) to use daemon threads in a ExecutorService
's thread pool? In particular I'm interested in describing the life-cycle of the VM shutdown with graceful shutdown (threads that have an interruption policy and play nicely) vs daemon threads.
Expanding that last point, finalize
on ThreadPoolExecutor
will call shutdown
on itself, but when it's using daemon threads, they could have terminated already if finalize
was called by the VM. What's the behavior of the thread pool then? Can it be tricked into remaining alive (and so not exiting the VM) if underlying threads terminated abruptly?
Part of the reason I'm asking is because i've seen it used to bypass the need to shutdown the actual ExectorService. Can you think of scenarios where bypassing its shutdown life-cycle can have ill affect? So far, the only reason I can come up with for using daemons is to take a short cut and I want to appreciate any unexpected side affects it could cause.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
发布评论
评论(3)
守护线程可能很有用,如果它们没有突然终止,在我看来它们就不会那么有用。
想必我们可以想象另一种类型的线程,当不再有正常线程运行时,该线程会被中断,而不是突然终止。这可能有点方便,但如果您必须进行任何清理,那么您可能想要进行有序清理。这会限制该功能的便利性。
另一方面,如果您有不需要在关闭时进行任何清理的任务,则守护线程非常方便。而且您不想浪费时间等待它们到达某个特定状态或冒关闭时挂起的风险等,因为您使用守护线程的原因是因为您不需要任何类型的清理。如果应用程序执行任何操作都会浪费时间。正在关闭。如果您关心,那么您不应该使用守护线程。
这与守护线程池没有什么不同。如果该线程池正在执行在关闭时不需要任何清理的任务,那么由于方便而有意义。
来自 JCiP 书籍:
应该谨慎使用守护进程线程,很少有处理活动可以随时安全地放弃而无需清理。特别是,将守护线程用于可能执行任何类型 I/O 的任务是危险的。守护线程最好保留用于“内务管理”任务,例如定期从内存缓存中删除过期条目的后台线程
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
如果发送到特定 ExecutorService 的任务可以突然终止,那么为什么不呢,这就是守护线程所做的。但一般来说,没有多少任务可以在没有关闭仪式的情况下终止,因此如果您选择守护线程,您必须知道自己在做什么。
当对象即将被垃圾回收时,
finalize()
被调用。无法保证任何特定对象何时(如果有)将被 GCd,ThreadPoolExecutor 也不例外,因此它的 Finalize() 可能会也可能不会被调用。该行为取决于特定的 JRE 实现,即使使用相同的实现,也可能会不时发生变化。If the tasks sent to that particular
ExecutorService
are ok to be terminated abruptly, then why not, that's what daemon threads do. But generally, there are not many tasks that are ok to be terminated with no shutdown ceremonies at all, so you must know what you're doing if you opt to daemon threads.finalize()
is called when an object is about to be garbage collected. There are no guarantees on when, if ever, any particular object will be GCd, andThreadPoolExecutor
is no exception, so itsfinalize()
may or may not be called. The behavior depends on the particular JRE implementation, and even with the same implementation, may vary from time to time.