在执行器服务上调用 shutdown() 的原因

2022-08-31 10:01:04

在过去的几个小时里,我读到了很多关于它的文章,我根本看不出有任何理由(有效的理由)来调用,除非我们有一个巨大的应用程序来存储,数十个不同的执行器服务,这些服务在很长一段时间内都没有使用。shutdown()ExecutorService

关机唯一(从我收集到的信息来看)是做正常线程完成后所做的事情。当普通线程完成 Runnable(或 Callable) 的 run 方法时,它将被传递给垃圾回收进行收集。使用执行器服务,线程将被简单地置于保留状态,它们不会被勾选用于垃圾回收。为此,需要关闭。

好的,回到我的问题。是否有任何理由经常调用关闭,甚至在提交某些任务后立即调用关闭?我想把有人正在做这件事的情况抛在脑后,然后打电话,因为这是经过验证的。一旦我们这样做了,我们就必须重新创建一个新的,做同样的事情。重用线程的整个想法不就是这样吗?那么,为什么要这么快就销毁呢?ExecutorServiceawaitTermination()ExecutorServiceExecutorServiceExecutorService

简单地创建(或根据您需要多少个)而不是一个合理的方法,然后在应用程序运行期间将任务传递给它们,一旦它们出现,然后在应用程序退出或其他一些重要阶段关闭这些执行器,这难道不是一种合理的方法吗?ExecutorService

我希望从一些有经验的编码人员那里得到答案,他们确实使用ExecutorServices编写了很多异步代码。

第二个问题,与Android平台的交易有点小。如果你们中的一些人会说,每次都关闭执行器和Android上的程序不是最好的主意,你能告诉我当我们处理应用程序生命周期的不同事件时,你如何处理这些关闭(具体来说 - 当你执行它们时)。

由于CommonsWare的评论,我使帖子中立。我真的没有兴趣争论到死,它似乎正在引领那里。我只有兴趣了解我在这里向有经验的开发人员提出的问题,如果他们愿意分享他们的经验。谢谢。


答案 1

该方法做一件事:防止客户端向执行器服务发送更多工作。这意味着除非执行其他操作,否则所有现有任务仍将运行到完成。即使对于计划任务也是如此,例如,对于计划执行者服务:计划任务的新实例不会运行。它还会释放所有后台线程资源。这在各种情况下都很有用。shutdown()

假设您有一个控制台应用程序,该应用程序具有运行 N 个任务的执行器服务。如果用户按 Ctrl-C,则希望应用程序终止,可能会正常终止。这优雅地意味着什么?也许您希望应用程序无法向执行器服务提交更多任务,同时希望等待现有的 N 个任务完成。您可以使用关闭钩子作为最后的手段来实现这一点:

final ExecutorService service = ... // get it somewhere

Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Performing some shutdown cleanup...");
        service.shutdown();
        while (true) {
            try {
                System.out.println("Waiting for the service to terminate...");
                if (service.awaitTermination(5, TimeUnit.SECONDS)) {
                    break;
                }
            } catch (InterruptedException e) {
            }
        }
        System.out.println("Done cleaning");
    }
}));

此挂接将关闭服务,这将阻止您的应用程序提交新任务,并等待所有现有任务完成,然后再关闭 JVM。等待终止将阻塞 5 秒,如果服务关闭,则返回 true。这是在循环中完成的,以便您确定服务最终将关闭。中断的异常每次都会被吞噬。这是关闭在整个应用程序中重用的执行程序服务的最佳方法。

此代码并不完美。除非您绝对肯定您的任务最终会终止,否则您可能希望等待给定的超时,然后退出,放弃正在运行的线程。在这种情况下,在超时后调用以最后尝试中断正在运行的线程是有意义的(还将为您提供等待运行的任务列表)。如果您的任务旨在响应中断,这将正常工作。shutdownNow()shutdownNow()

另一个有趣的场景是,当您有一个执行定期任务的 ScheduledExecutorService 时。停止周期性任务链的唯一方法是调用 。shutdown()

编辑:我想补充一点,我不建议使用关机钩子,如上图所示:它可能容易出错,并且应该只是最后的手段。此外,如果您注册了许多关机挂钩,则它们的运行顺序是不确定的,这可能是不希望的。我宁愿让应用程序显式调用 .shutdown()InterruptedException


答案 2

执行器服务的整个想法不是重用线程吗?那么,为什么要这么快就销毁遗嘱执行者服务呢?

是的。您不应频繁销毁和重新创建。在需要时进行初始化(主要是在启动时),并使其保持活动状态,直到完成为止。ExecutorServiceExecutorService

简单地创建 ExecutorService(或根据您需要多少个)进行组合,然后在应用程序运行期间将任务传递给它们,然后在应用程序退出或其他一些重要阶段关闭这些执行器,这难道不是一种合理的方法吗?

是的。在应用程序退出等重要阶段关闭是合理的。ExecutorService

第二个问题,与Android平台的交易有点小。如果你们中的一些人会说,每次都关闭执行器不是最好的主意,并且您在Android上编程,那么当我们处理应用程序生命周期的不同事件时,您能否告诉我如何处理这些关闭(具体而言,当您执行它们时)。

假设在应用程序中的不同活动之间共享。每个活动将以不同的时间间隔暂停/恢复,并且每个应用程序仍然需要一个。ExecutorServiceExecutorService

不要管理 活动生命周期方法中的 状态,而是将 Executor 服务管理(创建/关闭)移动到自定义服务ExecutorService

在服务中创建 = > 并在 中正确关闭它ExecutorServiceonCreate()onDestroy()

推荐的关机方式:ExecutorService

如何正确关闭 java ExecutorService


推荐