关闭和等待终止的首次调用有什么区别?

77

什么是区别?

ExecutorService eService = Executors.newFixedThreadPool(2);
eService.execute(new TestThread6());
eService.execute(new TestThread6());
eService.execute(new TestThread6());
eService.awaitTermination(1, TimeUnit.NANOSECONDS);
eService.shutdown();

eService.shutdown();
eService.awaitTermination(1, TimeUnit.NANOSECONDS);

我不太理解shutdown()方法。这个方法不等待之前提交的任务完成执行,这是什么意思呢?shutdown()是否可能终止已提交但未完成的任务?我尝试了一些例子,但它们并没有证明它,能否给我一个例子。


1
请查看以下链接:https://dev59.com/hVoV5IYBdhLWcg3wIb74#36644320 - Ravindra babu
只有在您发出关闭命令之前,才会终止操作。这是显而易见的吧?如果存在自动终止功能,就不需要关闭API了。 - user207421
10个回答

83

在使用awaitTermination方法之前,您应该先调用shutdown方法。否则,您可能需要等待很长时间,因为awaitTermination并不会真正关闭您的执行器。

如果您希望等待任务完成而不是等待执行器关闭,则应使用invokeAll方法。


5
awaitTermination方法实际上并没有先关闭你的执行器。因此,除非另一个线程关闭执行器,否则awaitTermination将一直等待,直到超时时间到期。显然,在你的例子中,在超时前只等待了1纳秒,这就是为什么你没有观察到“很长时间”的原因。 - C. K. Young
1
谢谢,我明白了。在shutdown() API中:这个方法不会等待之前提交的任务完成执行。这意味着可能会终止已经提交但尚未完成的任务吗?但是我尝试了一些例子并没有证明它。 - 王奕然
@user2245634 正确,任何未完成的任务都将被取消(它们的线程将被发送中断)。然而,如果您的任务不检查中断,那么取消当然就不起作用。 - C. K. Young
25
注意,shutdown() 方法不会中断已经执行的任务。根据 javadoc 的描述:"启动有序关闭过程,在该过程中,先前提交的任务将被执行,但不会接受新任务。" 如果你想要中断任务的执行,应该调用 shutDownNow() 方法。 - bowmore
2
@AmrishPandey 不,文档并不是这样说的。请参考S.D.的回答。你所描述的是shutdownNow的作用。 - Yngvar Kristiansen
显示剩余7条评论

57

阅读文档总是有帮助的:

shutdownNow:

尝试停止所有正在执行的任务,停止等待中的任务的处理,并返回一个执行等待的任务列表。这些任务会在从此方法返回时从任务队列中移除。

该方法不会等待正在执行的任务终止。请使用awaitTermination方法进行等待。

除了尽最大努力停止正在处理的活动任务外,没有其他保证。此实现通过Thread.interrupt()取消任务,因此任何无法响应中断的任务可能永远无法终止。

shutdown:

开始有序关闭,在此过程中,先前提交的任务将被执行,但不会接受新任务。如果已经关闭,则调用不会产生额外效果。

 

此方法不会等待先前提交的任务完成执行。使用awaitTermination方法进行等待。

awaitTermination:

 

在关闭请求后,阻塞直到所有任务完成执行或超时发生,或当前线程被中断,以先发生者为准。


26
我不理解关于关闭的文档。它首先说“已提交的任务将被执行”,然后又说“此方法不等待以前提交的任务完成执行。”那么,我的任务会完整地运行还是不会? - jmrah
32
意思是方法调用会立即返回。也就是调用shutdown的线程不会阻塞。 - S.D.
16
啊,原来是不阻塞啊。现在我明白了。他们应该用这个词而不是等待。谢谢。 - jmrah
1
@S.D.的回答略有修改。他们所说的“shutdown不等待之前提交的任务完成执行”,我相信他们的意思是当前线程不会阻塞,直到之前提交的任务完成执行。 - Jason

24

Shutdown意味着执行器服务不再接受更多的任务。

AwaitTermination是在关闭请求之后调用的。

您需要首先关闭服务,然后阻塞并等待线程完成。

如果您想要看到所有线程都完成运行,并坚持使用awaiTermination,则需要将超时参数设置为足够大。所以您可以这样做:

eService.shutdown();
if (!eService.awaitTermination(60000, TimeUnit.SECONDS))
    System.err.println("Threads didn't finish in 60000 seconds!");
}

或者,您可以这样做:

eService.shutdown();
while (!eService.isTerminated()) {

}

这样可以确保所有线程都能完成运行,除非它们被意外中断。


2
你可能是想说毫秒吧... 60000毫秒是16.666小时。 - Peter Perháč
34
不要像第二段代码片段那样进行忙等待。就是这样。不要。 - Alexander
2
正如 @Alexander 所指出的那样,您不应该使用第二个代码块。这种空体 while 循环被称为“忙等待”,会使 CPU 核心一直保持在 100% 直到完成。鉴于您有 awaitTermination 方法可用,这是一种浪费。 - Igor Rodriguez
1
@IgorRodriguez 是的。但我需要系统的最后一点性能。我必须对一个程序进行基准测试。 - ahrooran
1
@ahrooran 但第二个选项并不能保证更好的性能。您的线程池通常有足够的线程来使所有CPU保持繁忙状态。在调用awaitTermination时,线程的join()操作将阻塞主线程,直到所有执行器线程完成,以便不浪费更好地用于计算结果的CPU资源。由于中断,当线程终止时,主线程将立即被唤醒,时间限制只是一个上限。 - Frobeniusnorm
显示剩余3条评论

13

最佳实现:

executor.shutdown();
try {
    if (!executor.awaitTermination(3500, TimeUnit.MILLISECONDS)) {
        executor.shutdownNow();
    }                   
} catch (InterruptedException e) {              
    executor.shutdownNow();
}
        

1
这个答案正是我在寻找的。不过或许可以再多解释一些正在发生的事情。 - hayden.mumm
为什么不使用finally块来单独调用executor.shutdownNow()呢? - undefined

10

Main Difference

shutdown()-

1. Doesn't block the calling a thread i.e. the thread who called the shutdown().
2. Excecutor doesn't accept any new task after calling shutdown().

awaitTermination() -

1. Blocks the calling thread. (as join() method do)
疑惑点: 如果shutdown()不会杀死之前提交的任务,那为什么我们还需要awaitTermination()方法呢?

awaitTermination的意思是等待任务完成/终止,对吧?shutdown()也在做同样的事情——等待任何已经提交的任务以及正在运行的任务继续直到完成/终止,那么为什么还需要另一个方法awaitTermination(..)呢?以下是解释:

假设您只能等待10分钟来完成所有已提交的任务,并且在此之后想要调用shutdownNow()(---您已经知道它是做什么的了),然后在调用shutdown()之后使用awaitTermination(long timeout, TimeUnit unit)

注意awaitTermination(long timeout, TimeUnit unit)中的方法参数。这里的超时时间是关键。

如果没有时间限制,那么shutdown()就可以了。不需要awaitTermination()。


1
在我看来,与其使用awaitTermination(long timeout, TimeUnit unit),使用shutdown(long timeout, TimeUnit unit)会更加清晰易懂。这样就不需要先调用shutdown(),再调用awaitTermination(..)了。有没有人能够解释一下为什么要使用一个单独的方法而不是重载shutdown()的逻辑? - Dexter
如果awaitTermination退出块,但仍有任务在运行怎么办?在继续之前,如何确保池中的所有任务都被终止/杀死(如果超时到达)? - n1nsa1d00
正如我在答案中提到的,我们只需在调用shutdownNow()之前调用awaitTermination(),给线程池中的任务一些时间来执行或完成。如果它们在规定的时间内没有完成,您将关闭执行器。 - Ashwani Tiwari
如果在shutdown()之后使用awaitTermination(),那么它不是无用的吗? - Buffalo

3
executorService.execute(runnableTask);  

//executorService.shutdown(); //it will make the executorService stop accepting new tasks
//executorService.shutdownNow(); //tires to destroy the executorService immediately, but it doesn't guarantee that all the running threads will be destroyed at the same time. This method returns list of tasks which are waiting to be processed.
//List<Runnable> notExecutedTasks = executorService.shutdownNow(); //this method returns list of tasks which are waiting to be processed.developer decide what to do with theses tasks?

//one good way to shutdown the executorService is use both of these methods combined with the awaitTermination
executorService.shutdown();
try{
    if(!executorService.awaitTermination(1000, TimeUnit.MICROSECONDS)) {
        executorService.shutdownNow();
    }
}catch (InterruptedException e){
    e.printStackTrace();
}

3

在启动第一个任务后,ThreadPoolExecutor将启动一个线程,即使任务完成后也不会结束。至少对于固定的线程池来说是这样。这就是为什么我们需要调用shutdown。在关闭后,ThreadPoolExecutor将拒绝任何新任务,但会等待正在运行的任务完成,然后允许线程结束。这就是为什么我们需要在关闭后使用awaitTermination。


0

shutdown()会停止执行器接受更多的任务,并让正在执行的任务完成。该调用不会阻塞当前线程。

awaitTermination(1, TimeUnit.NANOSECONDS)将等待执行器在给定的时间内完成执行。请注意,这是一个阻塞调用。

如果您的优先级是在终止之前让执行器完成其执行,则简单地调用shutdown()即可完成工作。

然而,在某些情况下,我们可能希望在一定时间后关闭执行器,而不管任务是否完成执行。对于这些情况,您可以使用以下方法:

Runnable runnable = () -> System.out.println("Some tasks.");
ExecutorService eService = Executors.newFixedThreadPool(2);
eService.execute(runnable);
eService.execute(runnable);
eService.execute(runnable);
eService.execute(runnable);

eService.shutdown();
try {
    int waitCount = 0;
    // blocking the Thread for 1 minute. If awaitTermination() returns true then executor is shutdown
    while (!eService.awaitTermination(1, TimeUnit.MINUTES)) {
        waitCount++;
        if (waitCount == 60) { // waited for 60 minutes to complete execution
            List<Runnable> runnableList = eService.shutdownNow();
            System.out.println("ThreadPool forcefully shutdown with " + runnableList.size() + " tasks remaining.");
            break;
        }

    }
} catch (InterruptedException e) {
    eService.shutdownNow();
    System.err.println("ThreadPool shutdown interrupted.");
    Thread.currentThread().interrupt();
}

0

来自Java8 ThreadPool的awaitTermination方法:

    try {
        for (;;) {
            if (runStateAtLeast(ctl.get(), TERMINATED))
                return true;
            if (nanos <= 0)
                return false;
            nanos = termination.awaitNanos(nanos);
        }
    } finally {
        mainLock.unlock();
    }

它将首先检查线程池的运行状态。如果线程池没有关闭(将运行状态设置为终止),则awaitTermination方法将不会在超时之前返回。这就解释了为什么如果您先等待再关闭,那么等待时间会很长。


-2
你需要在调用 awaitTermination() 方法后立即调用 shutdownNow() 方法。只有这样,你才能找出 awaitTermination() 方法的实际用途。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接