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

使用Java ExecutorService,如何完成主动执行的任务,但停止处理等待的任务?,java,multithreading,Java,Multithreading,我使用ExecutorService(ThreadPoolExecutor)来运行(和排队)许多任务。我试图写一些尽可能优雅的关闭代码 ExecutorService有两种关闭方式: 我可以调用ExecutorService.shutdown(),然后调用ExecutorService.waitivetermination(…) 我可以调用ExecutorService.shutdownNow() 根据JavaDoc,shutdown命令: Initiates an orderly shutdo

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

ExecutorService有两种关闭方式:

  • 我可以调用
    ExecutorService.shutdown()
    ,然后调用
    ExecutorService.waitivetermination(…)
  • 我可以调用ExecutorService.shutdownNow()
  • 根据JavaDoc,
    shutdown
    命令:

    Initiates an orderly shutdown in which previously submitted
    tasks are executed, but no new tasks will be accepted.
    
    Attempts to stop all actively executing tasks, halts the
    processing of waiting tasks, and returns a list of the tasks that were
    awaiting execution.
    
    以及
    shutdownNow
    命令:

    Initiates an orderly shutdown in which previously submitted
    tasks are executed, but no new tasks will be accepted.
    
    Attempts to stop all actively executing tasks, halts the
    processing of waiting tasks, and returns a list of the tasks that were
    awaiting execution.
    
    我想要介于这两个选项之间的东西

    我想调用一个命令:
    A.完成当前活动的一项或多项任务(如
    关机
    )。
    B停止处理等待的任务(如
    shutdownow

    例如:假设我有一个具有3个线程的ThreadPoolExecutor。它当前在队列中有50个任务,前3个任务正在运行。我想允许这3项活动任务完成,但我不想启动其余47项任务

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

    我真的希望我错过了一些明显的东西,或者有一种干净的方法

    谢谢您的帮助。

    您需要的正是
    关机()。你错过了第一个单词的尝试和它的整个第二段:

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


    因此,只有在(!Thread.currentThread().isInterrupted())
    循环或其他情况下定期检查的任务才会终止。但是,如果您没有在任务中检查,它将继续运行。

    我最近遇到了这个问题。可能有一种更优雅的方法,但我的解决方案是首先调用
    shutdown()
    ,然后拉出
    ThreadPoolExecutor使用的
    BlockingQueue
    ,并对其调用
    clear()
    (或者将其排入另一个
    集合
    进行存储)。最后,调用
    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);
        }
    }
    
    publicstaticvoidshutdownpool(布尔等待终止)抛出InterruptedException{
    //调用shutdown以防止提交新任务
    executor.shutdown();
    //获取对队列的引用
    final BlockingQueue BlockingQueue=executor.getQueue();
    //清除队列
    blockingQueue.clear();
    //或者使用while循环将其内容复制到此处并删除()
    //等待活动任务完成
    如果(等待终止){
    执行器等待终止(关机超时,时间单位为秒);
    }
    }
    
    此方法将在directing类中实现,该类使用引用
    executor
    包装
    ThreadPoolExecutor

    请务必注意javadoc中的以下内容:

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


    这突出显示了这样一个事实,即当您排空任务时,可能会从
    阻塞队列
    轮询其他任务。但是,根据,所有的
    BlockingQueue
    实现都是线程安全的,因此这不会导致问题。

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

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

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

    我对这种方法的担心是,有时我确实想中断活动任务。他们目前确实会检查中断状态并正确处理中断异常,因为在紧急情况下(或在每日周期结束时),我需要立即关闭所有设备。在中断状态旁边添加一个额外的检查,以确定是否存在紧急状态。除了
    线程.isInterrupetd()
    ,还有其他中断点,包括
    Thread.sleep()
    Thread.join()
    ,和
    Object.wait()
    。@John:是的,引发
    中断异常的那些。然而,在执行者任务中使用它们中的任何一个都是非常罕见的。@BalusC:我认为这是一个有效且很好的答案,但我不同意在执行者任务中发现
    InterruptedException
    案例是非常罕见的。我的任务肯定会处理它们——有很多线程在等待第三方活动时暂停,以便为其他线程释放资源。事实上,我不明白你为什么不总是期待这种行为。
    InterruptedException
    似乎是专门为一个执行器同时运行多个线程的情况而设计的。+1很难理解为什么ExecutorService中不包含这样的功能。有时,语言/库开发人员似乎从未真正编写过任何多线程应用程序。耗尽队列、设置一些“中断”标志(或推送自杀请求)以及等待当前可运行程序完成似乎是一个合理且常见的要求。似乎是一个非常合理的要求。因为我订阅了并发兴趣邮件列表,所以我会在那里询问。我得到的建议与Unreputable和忽必烈汗的建议非常相似。检查isShutdown()或调用shutdown()并清除(或排空)队列