如何等到 ThreadPoolExecutor 的线程退出?
How to wait until a ThreadPoolExecutor's threads have exited?
注意:这里不是查看提交到线程池的任务是否完成,而是查看实际线程是否退出。
为了检测线程泄漏,我想要
形式的代码
val start = allThreads()
doStuff()
val end = allThreads()
assert start == end
然而,doStuff()
可能会使用一个或多个线程池,这些线程池实际上是通过调用shutdownNow()
或类似的方式清理的。 ThreadPoolExecutor 中似乎没有办法检测是否所有线程都已终止。 awaitTermination
之类的东西只能确保所有线程都已经到了一定会退出的地步,而不是已经到了。
理想情况下,解决方案不是 hacky,例如使用反射直接访问 class 的内部(当然我们可以从中获取所有线程,然后将它们全部加入)。
更新:这样做的原因
如果您在一段时间内开发了一个大型代码库,您可能会遇到线程泄漏问题。人们可能不会适当地关闭执行程序,人们可能只是调用 new Thread()
,人们可能会调用启动线程的第三方库,并且代码或库的代码不会退出这些线程。由于单个进程中的线程过多 运行,您的构建失败了,因为该进程可能 运行 数千个测试,每个测试都会泄漏一些线程。
为防止这种情况发生,您强制所有测试检查测试前的线程和测试后的线程是否为同一组。这可以做到,例如通过反射,验证每个测试 class 都继承自基础 class,然后在基础 class 中有 Before/After 来验证线程。细节不是
很重要,但基本上我们有一个线程泄漏检测器,无法通过泄漏线程的测试。
现在,如果您正确使用 ThreadPoolExecutor 并调用 shutdownNow,那么我们不希望测试因泄漏检测器而失败。但是,仅使用 shutdownNow 可能会导致误报,因为即使我们成功调用了 shutdownNow 并从其余代码返回并且处于我们检查当前线程的 After 阶段,线程池线程可能在那个时候仍然存在。因此我们想要一些方法来保证池中的线程在返回之前已经退出,以避免这些误报。
使用线程工厂。
ThreadFactory tf = runnable -> {
return new Thread(() -> {
try {
runnable.run();
} finally {
System.out.println(Thread.currentThread().getName()+": my thread exit");
}
});
};
ExecutorService svc = Executors.newFixedThreadPool(3, tf);
Future f = svc.submit(() -> System.out.println(Thread.currentThread().getName()+": some task"));
f.get();
svc.shutdown();
Thread.sleep(2000);
System.out.println("main done");
注意:这里不是查看提交到线程池的任务是否完成,而是查看实际线程是否退出。
为了检测线程泄漏,我想要
形式的代码val start = allThreads()
doStuff()
val end = allThreads()
assert start == end
然而,doStuff()
可能会使用一个或多个线程池,这些线程池实际上是通过调用shutdownNow()
或类似的方式清理的。 ThreadPoolExecutor 中似乎没有办法检测是否所有线程都已终止。 awaitTermination
之类的东西只能确保所有线程都已经到了一定会退出的地步,而不是已经到了。
理想情况下,解决方案不是 hacky,例如使用反射直接访问 class 的内部(当然我们可以从中获取所有线程,然后将它们全部加入)。
更新:这样做的原因
如果您在一段时间内开发了一个大型代码库,您可能会遇到线程泄漏问题。人们可能不会适当地关闭执行程序,人们可能只是调用 new Thread()
,人们可能会调用启动线程的第三方库,并且代码或库的代码不会退出这些线程。由于单个进程中的线程过多 运行,您的构建失败了,因为该进程可能 运行 数千个测试,每个测试都会泄漏一些线程。
为防止这种情况发生,您强制所有测试检查测试前的线程和测试后的线程是否为同一组。这可以做到,例如通过反射,验证每个测试 class 都继承自基础 class,然后在基础 class 中有 Before/After 来验证线程。细节不是 很重要,但基本上我们有一个线程泄漏检测器,无法通过泄漏线程的测试。
现在,如果您正确使用 ThreadPoolExecutor 并调用 shutdownNow,那么我们不希望测试因泄漏检测器而失败。但是,仅使用 shutdownNow 可能会导致误报,因为即使我们成功调用了 shutdownNow 并从其余代码返回并且处于我们检查当前线程的 After 阶段,线程池线程可能在那个时候仍然存在。因此我们想要一些方法来保证池中的线程在返回之前已经退出,以避免这些误报。
使用线程工厂。
ThreadFactory tf = runnable -> {
return new Thread(() -> {
try {
runnable.run();
} finally {
System.out.println(Thread.currentThread().getName()+": my thread exit");
}
});
};
ExecutorService svc = Executors.newFixedThreadPool(3, tf);
Future f = svc.submit(() -> System.out.println(Thread.currentThread().getName()+": some task"));
f.get();
svc.shutdown();
Thread.sleep(2000);
System.out.println("main done");