使用Java ExecutorService,如何完成主动执行任务但停止等待任务的处理?

时间:2011-11-16 20:51:42

标签: java multithreading

我使用ExecutorService(ThreadPoolExecutor)来运行(和排队)很多任务。我试图编写一些尽可能优雅的关闭代码。

ExecutorService有两种关闭方式:

  1. 我可以致电ExecutorService.shutdown()然后ExecutorService.awaitTermination(...)
  2. 我可以致电ExecutorService.shutdownNow()
  3. 根据JavaDoc,shutdown命令:

    Initiates an orderly shutdown in which previously submitted
    tasks are executed, but no new tasks will be accepted.
    

    shutdownNow命令:

    Attempts to stop all actively executing tasks, halts the
    processing of waiting tasks, and returns a list of the tasks that were
    awaiting execution.
    

    我想在这两个选项之间找到一些东西。

    我想叫一个命令:
    一个。完成当前活动的任务(如shutdown) 湾暂停等待任务的处理(如shutdownNow)。

    例如:假设我有一个包含3个线程的ThreadPoolExecutor。它目前在队列中有50个任务,前3个正在运行。我希望允许完成这3个活动任务,但我不希望剩下的47个任务开始。

    我相信我可以通过保留Future对象列表然后在所有对象上调用cancel来以这种方式关闭ExecutorService。但是,由于任务是从多个线程提交给此ExecutorService的,因此没有一种干净的方法可以执行此操作。

    我真的希望我错过一些明显的东西,或者有办法干净利落地做。

    感谢您的帮助。

3 个答案:

答案 0 :(得分:10)

我最近遇到过这个问题。可能有更优雅的方法,但我的解决方案是首先致电shutdown(),然后取出BlockingQueue使用的ThreadPoolExecutor并在其上调用clear()(或否则将其排放到另一个Collection进行存储。最后,调用awaitTermination()允许线程池完成当前的工作。

例如:

public static void shutdownPool(boolean awaitTermination) throws InterruptedException {

    //call shutdown to prevent new tasks from being submitted
    executor.shutdown();

    //get a reference to the Queue
    final BlockingQueue<Runnable> blockingQueue = executor.getQueue();

    //clear the Queue
    blockingQueue.clear();
    //or else copy its contents here with a while loop and remove()

    //wait for active tasks to be completed
    if (awaitTermination) {
        executor.awaitTermination(SHUTDOWN_TIMEOUT, TimeUnit.SECONDS);
    }
}

此方法将在引导类中使用引用ThreadPoolExecutor包裹executor来实现。

请注意ThreadPoolExecutor.getQueue() javadoc中的以下内容:

  

访问任务队列主要用于调试和   监控。此队列可能正在使用中。检索任务队列   不会阻止排队的任务执行。

这突出了这样一个事实,即在您排除其他任务时,可能会从BlockingQueue轮询其他任务。但是,根据that interface's documentation,所有BlockingQueue实现都是线程安全的,因此这不会导致问题。

答案 1 :(得分:5)

您可以使用一些额外的逻辑来包装每个提交的任务

wrapper = new Runnable()
    public void run()
        if(executorService.isShutdown())
            throw new Error("shutdown");
        task.run();

executorService.submit(wrapper);

额外检查的开销可以忽略不计。关闭执行程序后,仍会执行包装器,但原始任务不会执行。

答案 2 :(得分:4)

shutdownNow()正是您所需要的。您错过了第一个单词 Attempts 以及javadoc的整个第二段:

  

除了尽力尝试停止处理主动执行任务之外,没有任何保证。例如,典型的实现将通过Thread.interrupt()取消,因此任何未能响应中断的任务都可能永远不会终止。

因此,只有定期检查Thread#isInterrupted()的任务(例如在while (!Thread.currentThread().isInterrupted())循环或其他内容中)才会被终止。但如果您没有在任务中检查它,它仍将继续运行。