使用ExecutorService的Java应用程序从不关闭

使用ExecutorService的Java应用程序从不关闭,java,multithreading,executorservice,completable-future,Java,Multithreading,Executorservice,Completable Future,我正在尝试编写一个程序,将工作分配给几个java工作线程。问题是,当我从命令行运行它时,它永远不会返回。我没有得到提示,最终必须按ctrl-c键关闭程序 我已经把它简化为下面的一个小例子 import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future

我正在尝试编写一个程序,将工作分配给几个java工作线程。问题是,当我从命令行运行它时,它永远不会返回。我没有得到提示,最终必须按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");
    }       
}
就这样。它挂在那里。没有提示。如果我删除ExecutorService.submit行,我将获得

   djc@djc-linux:/local/mnt/workspace/TestExeServ/bin$ java TestExeServ
   Start
   done
   djc@djc-linux:/local/mnt/workspace/TestExeServ/bin$
节目自然结束

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

您必须调用或终止执行器的线程池。否则线程将保持活动状态,从而防止JVM终止

从类Javadoc:

ExecutorService可能会被关闭,这将导致它拒绝新任务。为关闭Executor服务提供了两种不同的方法。
shutdown()
方法允许在终止之前执行以前提交的任务,而
shutdownNow()
方法防止等待的任务启动并尝试停止当前正在执行的任务。终止后,执行者没有正在执行的任务,没有等待执行的任务,也不能提交新任务

请注意,作为最后一种手段,您正在使用的
ThreadPoolExecutor
将在垃圾收集时自动关闭-其
finalize()
方法调用
shutdown()
。但是,最好不要依赖于此并明确关闭执行器


正如@mre在评论中所说的,我将明确说明-您可以安全地在
run()
方法的末尾调用
exeServ.shutdown()
,因为
Future.get()
将被阻止,所以当代码执行在
get()
之后时,您不需要;我不再需要遗嘱执行人了。但是,如果这是更复杂的实际场景的简化版本,则可能需要找到正确的位置来安全地调用
shutdown()

您必须调用或终止执行器的线程池。否则线程将保持活动状态,从而防止JVM终止

从类Javadoc:

ExecutorService可能会被关闭,这将导致它拒绝新任务。为关闭Executor服务提供了两种不同的方法。
shutdown()
方法允许在终止之前执行以前提交的任务,而
shutdownNow()
方法防止等待的任务启动并尝试停止当前正在执行的任务。终止后,执行者没有正在执行的任务,没有等待执行的任务,也不能提交新任务

请注意,作为最后一种手段,您正在使用的
ThreadPoolExecutor
将在垃圾收集时自动关闭-其
finalize()
方法调用
shutdown()
。但是,最好不要依赖于此并明确关闭执行器



正如@mre在评论中所说的,我将明确说明-您可以安全地在
run()
方法的末尾调用
exeServ.shutdown()
,因为
Future.get()
将被阻止,所以当代码执行在
get()
之后时,您不需要;我不再需要遗嘱执行人了。但是,如果这是更复杂的实际场景的简化版本,则可能需要找到正确的位置来安全地调用
shutdown()

您需要关闭Executor或使用生成守护程序线程的线程工厂。或者设置标记allowCoreThreadTimeout您需要关闭Executor或使用生成守护程序线程的线程工厂。或者设置标记allowCoreThreadTimeout和
ShutdowNow
是可以的,因为
结果。get
将阻塞,直到任务完成完成,否则您将面临取消任务的风险。
shutdownow
可以,因为
result.get
将一直阻止,直到任务完成,否则您将面临取消任务的风险。
   djc@djc-linux:/local/mnt/workspace/TestExeServ/bin$ java TestExeServ
   Start
   done
   djc@djc-linux:/local/mnt/workspace/TestExeServ/bin$