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

Jef*_*erg 15 java multithreading

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

ExecutorService有两种关闭方式:

  1. 我可以打电话ExecutorService.shutdown()然后ExecutorService.awaitTermination(...).
  2. 我可以打电话ExecutorService.shutdownNow().

根据JavaDoc,shutdown命令:

Initiates an orderly shutdown in which previously submitted
tasks are executed, but no new tasks will be accepted.
Run Code Online (Sandbox Code Playgroud)

并且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.
Run Code Online (Sandbox Code Playgroud)

我想要这两个选项之间的东西.

我想调用一个命令:
  a.完成当前活动的任务(如shutdown).
  湾 停止等待任务的处理(如shutdownNow).

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

我相信我可以通过保持一个Future对象列表然后调用cancel所有这些来以这种方式关闭ExecutorService .但是由于任务是从多个线程提交给此ExecutorService的,因此没有一种干净的方法可以做到这一点.

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

谢谢你的帮助.

Pau*_*ora 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

此方法将在ThreadPoolExecutor包含引用的引导类中实现executor.

从ThreadPoolExecutor.getQueue()javadoc中注意以下内容非常重要:

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

这突出了这样一个事实,即可以在BlockingQueue排除其他任务时对其进行轮询.但是,BlockingQueue根据该接口的文档,所有实现都是线程安全的,因此这不会导致问题.


Bal*_*usC 5

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

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

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


irr*_*ble 5

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

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

executorService.submit(wrapper);
Run Code Online (Sandbox Code Playgroud)

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