Please help me find the reason for Thread leak in the code below. The TestThread
does not get garbage collected even after run() has completed (verified from the consoled print statement) and the main method has exited (verified from print statement and profiler tool).
The TestThread
, however, gets garbage collected if it is set as a Daemon Thread i.e. t.setDaemon(true)
. The code below is just a sample code which illustrates the problem in my application. I'm trying to use some pre-existing scheduling class (which was designed by someone else using ScheduledExecutorService
). I notice that when I keep scheduling multiple Runnable
s with the class, the created threads never get garbage collected.
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");
}
}
This is due to the fact that you are not calling shutdown()
on your executor service after you have scheduled your last job:
ses.schedule(...);
// this stops any management threads but existing jobs will still run
ses.shutdown();
I just added the shutdown()
call to your code and it exits fine. This is true of all ExecutorService
s. Without the shutdown the thread pool continues to wait for more jobs to be submitted and is never GC'd.
See @John's answer below for more details.
@Gray is correct with his assessment I just figure I add the why he is correct. The ExecutorService is a thread-pool which will reuse the threads.
Unlike new Thread(runnable).start();
when the run method completes the thread completes and will then be GC'd. When an Executor Runnable completes the thread will sit there and wait for another runnable task to be submitted and used. So by shutting down you are telling the executor to end all of the Threads in the thread pool.
To answer your last part. Setting it to daemon works only because there are no other (non-daemon) threads running. If your application started some other non daemon thread the Executor thread's will continue. Remember a daemon thread will be killed when only daemon threads are running.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With