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



我正在尝试编写一个程序,该程序将在几个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");
    }       
}

该程序的输出为

   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$

程序自然关闭。

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

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

ExecutorService类Javadoc:

可以关闭执行器服务,这将导致它拒绝新任务。提供了两种不同的方法来关闭执行程序服务。shutdown() 方法将允许以前提交的任务在终止之前执行,而 shutdownNow() 方法将阻止等待任务启动并尝试停止当前正在执行的任务。终止后,执行者没有正在执行的任务,没有等待执行的任务,并且无法提交新任务。

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


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

相关内容

  • 没有找到相关文章

最新更新