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