6

请帮我在下面的代码中找到线程泄漏的原因。即使在 run() 完成(从控制台打印语句验证)并且主方法已退出(从 print 语句和分析器工具验证)之后,TestThread也不会收集垃圾。

但是TestThread,如果将 设置为守护线程,即t.setDaemon(true). 下面的代码只是一个示例代码,它说明了我的应用程序中的问题。我正在尝试使用一些预先存在的调度类(它是由其他人设计的ScheduledExecutorService)。我注意到,当我继续Runnable用类调度多个 s 时,创建的线程永远不会被垃圾收集。

public class ThreadTest {

  static void runThreadWithExecutor() {
    final String name = "TestThread";
    ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor(
        new ThreadFactory() {
          @Override
          public Thread newThread(Runnable r) {
            Thread t = new Thread(r, name);
            t.setDaemon(false);
            return t;
          }
        });

    ses.schedule(new Runnable() {
        @Override
        public void run() {
           System.out.println("entered " + name);
           System.out.println("exiting " + name);
        }},
      2,
      TimeUnit.SECONDS);
    }

  public static void main(String[] args) throws InterruptedException {
    System.out.println("entered main");
    runThreadWithExecutor();
    Thread.sleep(5000);
    System.out.println("exiting main");
  }
}
4

2 回答 2

5

这是因为shutdown()您在安排最后一个工作后没有调用您的执行程序服务:

ses.schedule(...);
// this stops any management threads but existing jobs will still run
ses.shutdown();

我刚刚将shutdown()调用添加到您的代码中,它退出正常。所有ExecutorServices 都是如此。如果没有关闭,线程池将继续等待提交更多作业,并且永远不会被 GC。

有关更多详细信息,请参阅下面的@John 的答案。

于 2012-05-17T16:00:43.427 回答
3

@Gray 的评估是正确的,我只是想我补充一下他为什么是正确的。ExecutorService 是一个线程池,它将重用线程。

new Thread(runnable).start();run 方法完成时不同,线程完成然后将被 GC 处理。当 Executor Runnable 完成时,线程将坐在那里等待另一个可运行的任务被提交和使用。因此,通过关闭,您是在告诉执行程序结束线程池中的所有线程。

回答你的最后一部分。将其设置为守护程序仅是因为没有其他(非守护程序)线程在运行。如果您的应用程序启动了其他一些非守护线程,则 Executor 线程将继续。请记住,只有守护线程正在运行时,守护线程将被终止。

于 2012-05-17T16:10:56.023 回答