完成所有ExecutorService任务后,程序不会立即终止

ala*_*eno 25 java concurrency multithreading executorservice

我将一堆可运行的对象放入ExecutorService:

// simplified content of main method
ExecutorService threadPool = Executors.newCachedThreadPool();
for(int i = 0; i < workerCount; i++) {
    threadPool.execute(new Worker());
}
Run Code Online (Sandbox Code Playgroud)

我希望我的程序/流程在所有工人完成后立即停止.但根据我的日志,它需要另外20-30秒才能发生.工人们没有分配任何资源,事实上,他们现在什么都不做.

不要误会我的意思,这对我来说不是一个至关重要的问题,我只是想了解发生了什么,我想知道这是不是正常行为.

ysh*_*vit 28

Executors.newCachedThreadPool()使用Executors.defaultThreadFactory()它的ThreadFactory.defaultThreadFactory的javadocs说"每个新线程都是作为非守护线程创建的"(重点补充).因此,为其创建的线程newCachedThreadPool是非守护进程.这意味着它们会阻止JVM自然退出("自然地",我的意思是你仍然可以调用System.exit(1)或终止程序以使JVM停止).

应用程序完成的原因是每个线程在newCachedThreadPool超时内创建并在一段时间不活动后自行关闭.当最后一个关闭时,如果你的应用程序没有任何非守护程序线程,它将退出.

您可以(并且应该)ExecutorService通过shutdown或手动关闭shutdownNow.

另请参阅JavaDoc for Thread,其中讨论了守护进程.


Gra*_*ray 6

我希望我的程序/流程在所有工人完成后立即停止.但根据我的日志,它需要另外20-30秒才能发生.工人们没有分配任何资源,事实上,他们现在什么都不做.

问题是你没有关闭你的ExecutorService.将所有作业提交到服务后,应该关闭服务,否则JVM将不会终止,除非其中的所有线程都是守护程序线程.如果您没有关闭线程池,那么与ExecutorService(如果不是守护进程)相关联的任何线程都将阻止JVM完成.如果您已将任何任务提交到缓存的线程池,则必须等待线程超时并在JVM完成之前获取.

ExecutorService threadPool = Executors.newCachedThreadPool();
for(int i = 0; i < workerCount; i++) {
    threadPool.execute(new Worker());
}
// you _must_ do this after submitting all of your workers
threadPool.shutdown();
Run Code Online (Sandbox Code Playgroud)

将线程作为守护进程启动很可能不是您想要执行的操作,因为您的应用程序可能任务完成之前停止,并且所有任务将在此时立即终止.我刚刚进行了快速审核,并且ExecutorService在我们的生产代码中使用了178次,其中只有2个是作为守护程序线程启动的.其余的都正常关闭.

如果您需要ExecutorService在应用程序退出时强制停止,则使用shutdownNow()正确处理线程中断标志的顺序.

  • 你对"必须"的定义是什么?ExecutorService的javadoc并没有说它"必须"被关闭(只是它"可以"),你断言它将挂起(不是"可能"挂起)是对OP问题的重述,或者只是不正确,取决于您如何定义"挂起".我同意关闭`ExecutorService`这是一个好习惯,但是如果你打算在这个问题上做出这样的断言_并且这个断言三次,坦率地说我认为你需要支持它. (2认同)

Dan*_*Xie 5

基本上在 ExecutorService 上,您调用 shutdown() 然后调用 waitTermination():

ExecutorService taskExecutor = Executors.newFixedThreadPool(4);
while(...) {
   taskExecutor.execute(new MyTask());
}
taskExecutor.shutdown();
try {
  taskExecutor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
  ...
}
Run Code Online (Sandbox Code Playgroud)