如何等待ThreadPoolExecutor的线程退出?

时间:2018-04-30 23:08:00

标签: java threadpoolexecutor

注意:这不是试图查看已提交给线程池的任务是否已完成,而是实际线程是否已退出。

为了检测线程泄漏,我希望拥有

形式的代码
val start = allThreads()
doStuff()
val end = allThreads()
assert start == end

但是,doStuff()可能会使用一个或多个线程池,这些线程池实际上是通过调用shutdownNow()或类似内容来清理的。在ThreadPoolExecutor中似乎没有办法检测是否所有线程都已终止。像awaitTermination之类的东西只能确保所有线程都已经达到肯定会退出的程度,但不是已经有了。

理想情况下,解决方案不是hacky,例如使用反射来直接访问类的内部(当然我们可以获取所有线程然后将它们全部加入)。

更新:这样做的原因

如果您在一段时间内开发了大量代码库,则可能会遇到线程泄漏问题。人们可能不会恰当地关闭执行程序,人们可能只是调用new Thread(),人们可能会调用启动线程的第三方库,而代码或库的代码也不会退出这些线程。由于在一个进程中运行了太多线程,您的构建失败了,因为该进程可能会运行数千个测试,每个测试都会泄漏一些线程。

为了防止这种情况,你强制你的测试全部检查测试之前的线程和后面的线程是否是相同的集合。这可以通过例如完成。通过反射,验证每个测试类是否继承自基类,然后在具有Before / After的基类中验证线程。细节不是 很重要,但基本上我们有一个线程泄漏检测器,在泄漏的线程上测试失败。

现在,如果您正确使用ThreadPoolExecutor并调用shutdownNow,那么我们不希望测试因泄漏检测器而失败。但是,仅使用shutdownNow会导致误报,因为即使我们成功调用了shutdownNow并从其余代码返回并且处于After阶段,我们检查当前线程,那么线程池线程在那时仍然可能处于活动状态。因此,我们想要一些方法来保证池中的线程在返回之前已经退出,以避免这些误报。

1 个答案:

答案 0 :(得分:1)

使用线程工厂。

    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");