为什么使用与ScheduledExecutorService一起运行的线程不会收集垃圾

时间:2012-05-17 15:57:55

标签: java multithreading executorservice

请帮我在下面的代码中找到线程泄漏的原因。即使在run()完成后(从安慰的print语句中验证)并且main方法已退出(从print statement和profiler工具验证),TestThread也不会收集垃圾。

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

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

2 个答案:

答案 0 :(得分:5)

这是因为您在安排上一份工作后没有在执行人服务上调用shutdown()

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

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

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

答案 1 :(得分:3)

@Gray对他的评估是正确的我只是想我添加他为什么是正确的。 ExecutorService是一个将重用线程的线程池。

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

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