在ExecutorService上调用shutdown()的原因


84

我读它颇有几分在过去的几个小时,我根本看不到任何理由(有效的理由)来调用shutdown()ExecutorService,除非我们有一个巨大的应用程序,商店,几十个几十个不同的执行服务,不用于很长时间。

关闭(根据我的收集)唯一要做的事情是完成正常线程后所做的事情。当普通线程完成Runnable(或Callable)的run方法时,它将传递给Garbage Collection进行收集。使用Executor Service,线程将被简单地搁置,它们不会被选中以进行垃圾回收。为此,需要关机。

好,回到我的问题。有什么理由ExecutorService经常要求关机,甚至在提交某些任务后立即关机?我想抛弃有人正在做的情况,然后在awaitTermination()此之后立即调用,因为这已得到验证。一旦完成此操作,就必须重新创建一个新文件ExecutorService,以执行相同的操作。难道不是ExecutorService要重用线程的整个想法吗?那么,为什么要ExecutorService这么快就销毁呢?

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

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

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

基于CommonsWare的评论,我将该帖子设为中立。我真的对争论到死不感兴趣,而且看来它正在领先。我只想从经验丰富的开发人员那里了解到我在这里问的内容,如果他们愿意分享他们的经验。谢谢。


3
“我看到了很多示例代码,在这些示例代码中,在提交或执行任务之后,就一直有一个shutdown()调用” –随时使用超链接来提供您的索赔证据。就我个人而言,我从未见过任何能满足您要求的“示例代码”。您可能会误解某些内容,只有在我们知道您要检查的“样本代码”时,我们才能向您指出。
CommonsWare

4
嗨,CommonsWare。首先,我看到您对我的那种towards逼人的语气,但我认为这里没有证实。我不是想用消极的方式向别人肖像。至于您的报价,我主要是在谈论Java IV版中的“多任务处理”部分。您可以在Bruce Eckel的示例中找到许多实例。它们大多很简单,但布鲁斯给我的印象是经常使用关机。无论如何,您都专注于那些不是我帖子主要内容的内容。我删除了那些部分,因为我真的不想争论。
卢卡斯

1
hay @CommonsWare在Bruce Eckel..in并发/执行器第804页的Java书籍中的思考中,他总是在提交或执行简单应用程序中的任务后立即使用shutdown()方法来说明执行器的工作方式,如卢卡斯所说
错误

2
我知道这是一个旧帖子,但是我认为OP的问题仍然有效。我还遇到了许多示例代码,其中“在execute()之后立即有一个shutdown()调用”。tutorials.jenkov.com/java-util-concurrent/executorservice.html(当您使用Google“ java executorservice示例”时出现的第一个教程)
baekacaek 2015年

谢谢,我对这些“示例代码”也提出了同样的问题。 journaldev.com/2340/…–
Gregordy

Answers:


57

shutdown()方法做一件事:防止客户端将更多工作发送给执行者服务。这意味着除非采取其他措施,否则所有现有任务仍将运行完成。即使对于计划任务也是如此,例如对于ScheduledExecutorService:计划任务的新实例将不会运行。这在各种情况下都非常有用。

假设您有一个控制台应用程序,该应用程序具有运行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。这是循环执行的,因此您可以确定服务最终将关闭。每次都会吞下InterruptedException。这是关闭执行程序服务的最佳方法,该服务在整个应用程序中都可以重用。

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

另一个有趣的情况是当您具有执行定期任务的ScheduledExecutorService时。停止周期性任务链的唯一方法是呼叫shutdown()

编辑:我想补充一点,在一般情况下,我不建议使用如上所示的关机钩子:它可能容易出错,并且应仅是不得已而为之。此外,如果您注册了许多关闭挂钩,则它们的运行顺序是不确定的,这可能是不希望的。我宁愿让应用程序显式调用shutdown()InterruptedException


抱歉,乔瓦尼(Giovanni)的回复很晚,谢谢您的回复。是的,我知道执行器的工作原理,我试图在问题中进行解释。关闭功能可以执行您所说的操作,还可以使垃圾收集器收集这些死线程,并实际上收集ExecutorService实例。我的问题很具体。在ExecutorService上提交/执行任何操作之后,是否有任何理由一直调用“ shutdown()”。问题的第二部分严格来说与Android体系结构有关。如果上一个答案为否,那么在和之间何时调用shutdown。生命周期。
卢卡斯

3
没有理由一直调用shutdown()。实际上,这可能绝对是错误的做法,因为这将阻止您再次重用执行程序服务。在服务生命周期结束时调用它的原因是,您可以注意到,最终可以对线程进行垃圾回收。如果您不这样做,那么即使这些线程处于空闲状态,它们也将使JVM保持活动状态。
Giovanni Botta

“没有理由一直调用shutdown()。实际上,这绝对是错误的选择,因为这将阻止您再次使用执行程序服务”。这正是我的推理,也是我对原始问题的疑惑。重申一下,问题是:我应该何时在Android生命周期中关闭ExecutiveService?
卢卡斯

2
我没有Android经验,但是我想您应该在应用程序关闭时将其关闭,以允许JVM最终退出。
Giovanni Botta 2013年

3
我懂了。我建议您使用缓存的线程池,并且永远不要调用shutdown()它,以免在不必要时不浪费资源。如果应用程序确实关闭,则池中的线程最终将被垃圾回收(默认情况下空闲60秒之后)。请注意,如果要限制池的范围或想要不同的线程生存时间,则可以ThreadPoolExecutor直接创建一个。
Giovanni Botta 2013年

13

难道不是ExecutorService重用线程的全部想法?那么,为什么要这么快就销毁ExecutorService呢?

是。您不应该ExecutorService经常销毁和重新创建。ExecutorService在需要时进行初始化(通常在启动时进行),并使其保持活动状态,直到完成操作为止。

简单地创建ExecutorService(或根据您需要的数量而定),然后在应用程序运行期间将任务传给他们,然后在应用程序退出或其他一些重要阶段将其关闭时,这是一种合理的方法吗?遗嘱执行人?

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

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

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

无需管理“ExecutorService活动”生命周期方法的状态,而是将ExecutorService管理(“创建/关闭”)移至您的自定义服务

ExecutorService在服务中创建=>onCreate()并在以下位置正确关闭它onDestroy()

推荐的关闭方式ExecutorService

如何正确关闭Java ExecutorService


3

一旦不再需要ExecutorService释放系统资源并允许其正常关闭应用程序,则应将其关闭。因为ExecutorService中的线程可能是非守护程序线程,所以它们可能阻止正常的应用程序终止。换句话说,您的应用程序在完成其主要方法后仍保持运行状态。

参考书

第14章第814页


0

在ExecutorService上调用shutdown()的原因

今天,我遇到了这样一种情况,我必须等到一台机器准备好之后,才能在该机器上启动一系列任务。

我对此机器进行了REST调用,如果我没有收到503(服务器不可用),则表明该机器已准备就绪,可以处理我的请求。因此,我等到第一个REST调用获得200(成功)。

有多种方法可以实现它,我使用ExecutorService创建了一个线程,并安排它每X秒运行一次。所以,我需要在某种情况下停止该线程,检查一下...

final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
    Runnable task = () -> {
        try {
            int statusCode = restHelper.firstRESTCall();

            if (statusCode == 200) {
                executor.shutdown();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    };

    int retryAfter = 60;
    executor.scheduleAtFixedRate(task, 0, retryAfter, TimeUnit.SECONDS);

第二个问题,与android平台的交易要小一些。

也许我能回答您是否会提供更多背景信息!同样,根据我在Android开发中的经验,很少需要使用线程。您是否正在开发需要性能线程的游戏或应用程序?如果不是这样,在Android中,您还有其他方法可以解决问题,例如我上面解释的情况。您可以根据上下文使用TimerTask,AsyncTask或处理程序或加载程序。这是因为如果UIThread等待很长时间,您就会知道会发生什么:/


0

尽管对于计划内的工作(例如,对于ScheduledExecutorService),这是真实的:预定任务的新案例不会运行。

我们应该期望您有一个舒适的应用程序,该应用程序具有运行N ergent的代理程序管理。

我没有毫不费力地抓住它的意思吗?也许您需要您的应用程序不具有向代理人管理提交更多任务的选项,同时您需要坐下来完成当前的N个任务。

除非您对自己的差事最终抱有完全肯定的态度,否则您应该在给定的休息时间保持坐稳,然后简单地退出,放弃运行的弦乐。

如果您的活动旨在对干扰做出反应,则可以正常工作。

另一个有趣的情况是您拥有一个ScheduledExecutorService来播放活动。

停止活动链的最佳方法是调用shutdown()

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.