我正在尝试编写一个程序,将多个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$
该计划自然结束。
我是否需要在ExecutorService上执行一些我无法正常执行的清理任务?我假设.get()调用加入了线程。那不是这样吗?
答案 0 :(得分:5)
您必须调用ExecutorService#shutdown()
或ExecutorService#shutdownNow()
来终止执行程序的线程池。否则线程将保持活动状态,从而阻止JVM终止。
来自ExecutorService
类Javadoc:
可以关闭ExecutorService,这将导致它拒绝新任务。提供了两种不同的方法来关闭ExecutorService。
shutdown()
方法将允许先前提交的任务在终止之前执行,而shutdownNow()
方法阻止等待任务启动并尝试停止当前正在执行的任务。终止时,执行程序没有正在执行的任务,没有等待执行的任务,也没有新的任务可以提交。
请注意,作为最后的手段,您使用的ThreadPoolExecutor
会在收集垃圾时自行关闭 - 其finalize()
方法会调用shutdown()
。但是,最好不要依赖它并明确关闭执行程序。
正如@mre在评论中说明的那样,我会明确指出 - 您可以安全地在exeServ.shutdown()
方法结束时致电run()
,因为Future.get()
会阻止,所以代码执行是在get()
之后你不再需要执行者了。但是,如果这是更复杂的现实场景的简化版本,您可能需要找到安全调用shutdown()
的正确位置。