【问题标题】:Daemon threads scheduled on an ExecutorService; explain why this is bad form在 ExecutorService 上调度的守护线程;解释为什么这是不好的形式
【发布时间】:2012-01-06 20:55:56
【问题描述】:

我对在使用ExectuorService 调度的线程上有序关闭的想法感到满意;也就是说,调用shutdownshutdownNow会导致池上创建的线程优雅退出。如果他们回复interrupt,您可以确定 finally etc 将被调用,您将获得一个干净、可预测的退出(您可以在其中清理任何资源等)。

但是,如果您已将线程设置为守护进程(通过执行程序的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 的线程池中使用守护线程是不好的做法(或者如果您不同意)?特别是我有兴趣通过优雅关闭(具有中断策略并且运行良好的线程)与守护线程来描述 VM 关闭的生命周期。

扩展最后一点,ThreadPoolExecutor 上的finalize 将在其自身上调用shutdown,但是当它使用守护线程时,如果 VM 调用了finalize,它们可能已经终止。那么线程池的行为是什么?如果底层线程突然终止,它是否可以被欺骗以保持活动状态(因此不退出 VM)?

我问的部分原因是因为我已经看到它用来绕过关闭实际 ExectorService 的需要。你能想到绕过 its 关闭生命周期会产生不良影响的场景吗?到目前为止,我能想到的使用守护进程的唯一原因是走捷径,我想了解它可能导致的任何意想不到的副作用。

【问题讨论】:

    标签: java multithreading concurrency daemon executorservice


    【解决方案1】:

    在 ExecutorService 的线程池中使用守护线程是不好的做法吗?

    如果发送到特定ExecutorService 的任务可以被突然终止,那为什么不呢,这就是守护线程所做的。但一般来说,没有多少任务可以在没有关闭仪式的情况下终止,所以如果你选择守护线程,你必须知道你在做什么。

    finalize() 在对象即将被垃圾回收时调用。不能保证任何特定对象何时会被 GCd,ThreadPoolExecutor 也不例外,所以它的finalize() 可能会或可能不会被调用。行为取决于特定的 JRE 实现,即使使用相同的实现,也可能会不时发生变化。

    【讨论】:

    • 当然,将线程池本身设置为始终使用守护线程会限制其使用?你会说任何被安排的线程都必须知道并且可以接受它......所以如果它用于框架代码,它可能会受到限制。
    • @Toby:使用肯定是有限制的,所以不应该作为通用的解决方案。至少该特定池的入口点应该用警告标志强烈标记。
    【解决方案2】:

    守护线程可能很有用,如果它们不被突然终止,它们就不会在 IMO 中那么有用。

    大概我们可以想象另一种类型的线程,它在没有正常线程运行时被中断,而不是被突然终止。这可能有点方便,但如果您必须进行任何清理,您可能想要进行有序清理。这将限制此功能的便利性。

    另一方面,如果您的任务在关机时不需要任何清理,则守护线程非常方便。而且您不想浪费时间等待它们到达某个特定状态或冒险挂断关机等,因为您使用守护线程的原因是因为您不需要任何类型的清理。如果应用程序执行任何操作都是浪费时间。正在关闭。如果你在乎,那么你不应该使用守护线程。

    与守护线程池没有什么不同。如果该线程池正在执行不需要在关闭时进行任何清理的任务,那么由于方便,这将是有意义的。

    来自JCiP book

    应该谨慎使用守护线程,很少有处理活动可以随时安全地放弃而无需清理。特别是,将守护线程用于可能执行任何类型 I/O 的任务是很危险的。守护线程最好保存用于“管家”任务,例如定期从内存缓存中删除过期条目的后台线程

    【讨论】:

      【解决方案3】:

      我倾向于为守护线程和非守护线程使用不同的池。守护程序池倾向于重复执行清理工作、监控和后台任务,如果不执行一两个则无关紧要。任何仅在应用程序仍在运行时才有意义的任务都可以创建守护线程任务。例如GC 线程是守护线程。

      【讨论】:

      • 当然,但是当您可能以编程方式关闭表现良好的池时,为什么要允许它们在没有清理的情况下终止(例如)?比如,为什么在杂项任务/清理池上调用shutdown而不将它们作为守护进程。
      • 因为它是开发人员需要知道的额外内容,但没有任何区别,因此可能不会始终如一地完成。
      • 嗯,没有真正看到...如果您不关闭执行程序,VM 将不会退出。我在想开发人员已经知道他现在需要调用 shutdown/Now 那么为什么要绕过这个不需要关闭的特殊情况池。我希望人们在两者上都调用关机..?
      • 后台任务通常由不同的开发人员编写的库执行,而不是编写执行关闭的主要代码的开发人员。如果您有多个开发人员,他可能不知道关闭隐藏在库中的执行程序。
      猜你喜欢
      • 1970-01-01
      • 2010-09-16
      • 2016-07-10
      • 1970-01-01
      • 1970-01-01
      • 2012-03-09
      • 2011-01-13
      相关资源
      最近更新 更多