Java 使用 ExecutorService 的应用程序永远不会关闭

Java application that uses ExecutorService never closes

我正在尝试编写一个程序,将工作分配给几个 java 工作线程。问题是,当我从命令行 运行 它永远不会 returns。我没有收到提示,最终必须按 ctrl-c 关闭程序。

我已将其简化为以下简单案例

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class TestExeServ {

    private class ExpensiveTask implements Callable<Integer>{
        private final String msg; 
        public ExpensiveTask(String str){
            this.msg = str;
        }

        @Override
        public Integer call()  {
            System.out.println( "My message was " + msg);
            return 1;
        }
    }

    private void run()
    {
        final ExecutorService exeServ = Executors.newFixedThreadPool(2);
        Future<Integer> result = exeServ.submit(new ExpensiveTask("Hello!") );
        try {
            System.out.println( " Task is done, it returned " + result.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }   

    public static void main(String args[])
    {
        System.out.println( "Start");       

        TestExeServ tes = new TestExeServ();
        tes.run();
        System.out.println( "Done");
    }       
}

这个程序的输出是

   djc@djc-linux:/local/mnt/workspace/TestExeServ/bin$ java TestExeServ
   Start
   my message was Hello!
    Task is done, it returned 1
   done

就是这样。它挂在那里。没有提示。如果我删除 ExecutorService.submit 行,我会得到

   djc@djc-linux:/local/mnt/workspace/TestExeServ/bin$ java TestExeServ
   Start
   done
   djc@djc-linux:/local/mnt/workspace/TestExeServ/bin$

程序自然关闭。

我是否需要在 ExecutorService 上执行一些我没有正确执行的清理任务?我假设 .get() 调用加入了线程。不是这样吗?

您必须调用 ExecutorService#shutdown() or ExecutorService#shutdownNow() 来终止执行程序的线程池。否则线程将保持活动状态,从而防止 JVM 终止。

来自 ExecutorService class Javadoc:

An ExecutorService can be shut down, which will cause it to reject new tasks. Two different methods are provided for shutting down an ExecutorService. The shutdown() method will allow previously submitted tasks to execute before terminating, while the shutdownNow() method prevents waiting tasks from starting and attempts to stop currently executing tasks. Upon termination, an executor has no tasks actively executing, no tasks awaiting execution, and no new tasks can be submitted.

请注意,作为最后的手段,您使用的 ThreadPoolExecutor 会在垃圾回收时自行关闭 - 它的 finalize() 方法会调用 shutdown()。但是,最好不要依赖于此并显式关闭执行程序。


正如@mre 在评论中所述,我会明确说明 - 您可以安全地在 run() 方法的末尾调用 exeServ.shutdown(),因为 Future.get() 会阻塞,因此,当代码执行在 get() 之后时,您不再需要执行程序。但是,如果这是更复杂的现实生活场景的简化版本,您可能需要找到安全调用 shutdown().

的正确位置