2012-05-17 103 views
5

请帮我看下面代码中线程泄漏的原因。即使在run()完成后(从安慰的打印语句验证)并且主方法已退出(从打印语句和分析器工具验证),TestThread也不会收集垃圾回收。为什么运行ScheduleExecutorService的UserThread不会被垃圾收集

但是,如果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"); 
    } 
} 

回答

5

这是由于这样的事实,你是不是要求您执行服务shutdown()已计划后,你的上一份工作:

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

我刚添加的shutdown()调用你的代码,并退出精细。在所有ExecutorService s中都是如此。如果没有关闭,线程池将继续等待更多的作业提交并且从不GC'd。

请参阅下面的@ John的答案以获取更多详细信息。

3

@格雷对他的评估是正确的我只是想补充他为什么是正确的。 ExecutorService是一个将重用线程的线程池。

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

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

+2

好信息约翰。一个调整。 'shutdown()'不会结束线程池中的所有线程,直到所有作业已提交完成。 – Gray

+0

@格雷好点 –

+0

谢谢你们。虽然我最初选择这个作为我接受的答案,但后来我选择了格雷的答案,因为如果没有格雷的答案,这个答案就不言自明。但是,这个回应给了我充分的解释,需要有信心修改代码:)并且它可以工作! – Kes115