101

在过去的几个小时里,我读了很多关于它的文章,我根本看不出有任何理由(正当理由)调用shutdown().ExecutorService使用了很长时间。

唯一的事情(据我所知)关闭所做的就是做一个普通线程在完成后所做的事情。当普通的 Thread 完成 Runnable(或 Callable)的 run 方法后,会被传递给 Garbage Collection 进行收集。使用 Executor Service,线程将被搁置,不会为垃圾收集打勾。为此,需要关闭。

好的,回到我的问题。是否有任何理由ExecutorService经常调用关机,甚至在提交一些任务后立即调用?我想留下有人正在这样做的情况,并在此之后立即调用,awaitTermination()因为这是经过验证的。一旦我们这样做了,我们必须重新创建一个新ExecutorService的,做同样的事情。ExecutorService重用线程的整个想法不是吗?那么为什么ExecutorService这么快就毁掉了呢?

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

我想从一些使用 ExecutorServices 编写大量异步代码的经验丰富的程序员那里得到答案。

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

由于 CommonsWare 的评论,我使帖子保持中立。我真的没有兴趣争论到死,而且似乎它正在导致那里。我只对了解我在这里向经验丰富的开发人员提出的问题感兴趣,他们是否愿意分享他们的经验。谢谢。

4

5 回答 5

65

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 之前等待所有现有任务完成。await 终止将阻塞 5 秒,如果服务关闭则返回 true。这是在循环中完成的,因此您确定服务最终会关闭。InterruptedException 每次都被吞没。这是关闭在整个应用程序中重用的执行程序服务的最佳方法。

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

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

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

于 2013-05-24T19:21:03.097 回答
14

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

是的。ExecutorService你不应该经常破坏和重新创建。在需要时初始化ExecutorService(主要是在启动时)并保持激活状态,直到完成。

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

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

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

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

与其管理ExecutorServiceActivity 生命周期方法中的状态,不如将 ExecutorService 管理(创建/关闭)移至您的自定义Service

ExecutorService在 Service => 中创建onCreate()并正确关闭它onDestroy()

推荐的关机方式ExecutorService

如何正确关闭 java ExecutorService

于 2017-08-26T07:15:02.660 回答
6

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

参考书

第 14 章 页码:814

于 2018-04-29T20:34:29.263 回答
1

在 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 或 Handlers 或 Loaders。这是因为如果 UIThread 等待很长时间,您就会知道会发生什么:/

于 2017-09-27T22:43:41.953 回答
0

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

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

我没有毫不费力地理解它的含义?也许您需要您的应用程序不能选择向代理管理提交更多任务,同时您需要坐好等待您当前的 N 项任务完成。

除非您完全确定您的差事最终会完成,否则您应该在给定的休息时间坐稳,然后简单地退出,抛弃正在运行的字符串。

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

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

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

于 2019-05-19T05:37:31.123 回答