Java 如何等待线程池执行器';s线程已退出?

Java 如何等待线程池执行器';s线程已退出?,java,threadpoolexecutor,Java,Threadpoolexecutor,注意:这并不是试图查看提交到线程池的任务是否已完成,而是查看实际线程是否已退出 为了检测螺纹泄漏,我想有一个代码的形式 val start = allThreads() doStuff() val end = allThreads() assert start == end 但是,doStuff()可能会使用一个或多个线程池,这些线程池实际上是通过调用shutdownNow()或类似方法清理的。在ThreadPoolExecutor中似乎没有检测是否所有线程都已终止的方法。像等待终止这样的事情

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

为了检测螺纹泄漏,我想有一个代码的形式

val start = allThreads()
doStuff()
val end = allThreads()
assert start == end
但是,
doStuff()
可能会使用一个或多个线程池,这些线程池实际上是通过调用
shutdownNow()
或类似方法清理的。在ThreadPoolExecutor中似乎没有检测是否所有线程都已终止的方法。像
等待终止
这样的事情只能确保所有线程都达到了一定的退出点,而不是它们已经退出了

理想情况下,该解决方案不是黑客式的,比如使用反射直接访问类的内部(当然,我们可以从中获得所有线程,然后将它们全部连接起来)

更新:这样做的原因是什么

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

为了防止出现这种情况,您必须强制所有测试都检查测试前的线程和测试后的线程是否是同一组。这可以通过反射、验证每个测试类是否从基类继承来完成,然后在基类中使用Before/After来验证线程。细节不清楚 重要的是,基本上我们有一个线程泄漏检测器,它不能通过对泄漏线程的测试

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

使用线程工厂

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

CountDownLatch
?线程是否真的被销毁是线程池设计用来隐藏的抽象概念。了解这一点有什么好处?如果您需要知道某些任务是否已完成执行,那么您可能应该使用
ExecutorService
,因为它可以返回
Future
对象。@VGR如上所述,以确保不会泄漏线程。e、 g.缺少对
shutdownNow()
的调用,或者缺少执行某些操作或其他操作的原始线程。我当然可以看到一些标志,告诉你除了jvm内存之外的所有资源是否都已释放,这并没有真正破坏抽象,并且足够有用,值得使用。@D.B.正如问题中提到的,我的目标不是查看某些任务是否已完成运行,我已经非常清楚了。嗯,很有趣,我想如果您跟踪ThreadFactory创建的线程,那么您可以在最后加入它们。想知道它的缺点…我没有争论你的问题,但我无法想象有什么理由要等待这些线程的死亡。要么你误用了executor服务,要么你没有说一些非常具体的事情。shutdownNow()方法正在做它们应该做的事情,它们失败的唯一原因是如果您的任务不可中断或没有及时结束,这是您应该知道的另一种方式,因为您提交了这些任务。shutdownNow仅使线程处于即将退出的位置。现在,如果您试图检测一大块代码中的线程泄漏,如果您在关闭执行器后不久检查是否存在泄漏,您可能会得到误报。现在,为什么要检查泄漏?如果您始终正确使用执行器,并在使用后将其关闭,则无需。然而,在一个大的代码库中,你想要检测到有人使用原始线程和他们可能使用的任何其他线程,而没有适当地清理它们。@Jamie:好的。我明白了。您正在添加运行时复杂性以监视设计时问题。但是,如果您无法控制他们将在何处创建新的exec服务,那么您也就没有机会设置线程工厂或拦截任何内容。不能禁止使用安全管理器创建线程(如果可能的话),这太严格了。单元测试可能更具攻击性,但同样,糟糕的代码已经漏掉了,所以测试不足也会造成问题。在我看来,您需要轮询MXThreadBean以获取过高的线程计数和警报。我正在使用单元测试中的Before和After来获取Before和After的线程列表,并对它们进行比较,因此无论发生什么情况,线程都将得到检查。螺纹工厂的唯一一点是确保螺纹及时退出,从而不会导致检漏仪误报。