10/15/2012

Java Applicatioin Process Hangs on Windows and Cached Thread Pool

The following java test app runs and terminates normally on Unix but hangs on Windows.

import java.util.concurrent.*;

public class ThreadPoolTest {
    private static ExecutorService es = Executors.newCachedThreadPool();

    public static void main(String args[]) { 
        es.submit(new Task("## running task 1..."));
        es.submit(new Task("## running task 2..."));
        System.out.println("## after tasks, in main");
    }

    private static class Task implements Runnable {
        private String msg;

        private Task(String s) {
            this.msg = s;
        }

        @Override public void run() {
            System.out.println(msg);
        }
    }
}
The direct reason for hanging is the 2 worker threads were returned to the cached thread pool after finishing the printing task, and stayed alive and idle indefinitely. Pooled threads by default are non-daemon threads. A Java program will terminate only after all non-daemon threads have terminated. Although the main thread in the above test app have finished its job, the Java process will not terminate due to the 2 live worker threads.

A couple of solutions and their pros and cons:

1, So shall we make the pooled threads daemon threads to fix it? Be careful here, since the Java process may just terminate prematurely, immediately after the main thread is done, but before daemon workers complete their work. Since they are daemon threads, their task status is totally ignored in exiting JVM.

If you really want to take the daemon approach, the application will need some logic to poll the stask status, and wait for their completion before exiting the main thread.

2, How about explicitly call ExecutorService.shutdown() method? After all, a task implementing app logic is not a daemon thread, and should be marked as such. Shutting down the thread pool (the actual type of ExecutorService in our example) makes more sense. But pay attention to all possible exit paths and make sure shutdown guaranteed in all paths, such as normal completion, throwable.

If the thread pool is used by various parts of a large application, how do you coordinate them such that shutdown is called only after all clients are finished? Compared to approach 1, additional coordiation (e.g., with wait/notify or CountDownLatch) is needed. To fix the hang using shutdown():

import java.util.concurrent.*;

public class ThreadPoolTest {
    private static ExecutorService es = Executors.newScheduledThreadPool(2);

    public static void main(String args[]) throws InterruptedException {
        es.submit(new Task("# running task 1..."));
        es.submit(new Task("# running task 2..."));
        es.shutdown();

        //block here for all tasks to finish before proceeding in the main thead.
        //if you want to enforce the execution order. optional.
        es.awaitTermination(1, TimeUnit.DAYS);
        System.out.println("# after tasks, in main");
    }

    private static class Task implements Runnable {
        private String msg;

        private Task(String s) {
            this.msg = s;
        }

        @Override public void run() {
            try {
                Thread.sleep(1000*30);
            } catch (InterruptedException e) {
            }
            System.out.println(msg);
        }
    }
}

awaitTermination is totally optional. With it, you will see 2 task output before main thread output:
# running task 2...
# running task 1...
# after tasks, in main

Without it, main thread output comes before task output:
# after tasks, in main
# running task 2...
# running task 1...

3, Is there a default idle timeout for worker threads, so applications don't have to manage the shutdown? Yes, there is a default idle timeout 60 seconds for non-core threads. When creating a thread pool, you can specify the number of core threads, along with other parameters. By default core threads do not time out after becoming idle. But you can certainly make core threads subject to idle timeout by calling threadPool.allowCoreThreadsTimeout(true).

How does this apply to our test app? We created the thread pool by calling newCachedThreadPool() without passing any parameters. By default, the pool is created with 0 core threads and 60-second idle timeout. So any worker threads in the test app should be non-core and should automatically time out after 60 seconds. Yes, that is what happened on Mac OS or Linux. But on windows 7, the same program just hangs.

Do you still want to rely on its default idle timeout, which seems sensitive to OS?

4, Use Thread and Runnable class directly for simple use cases.  When you only need threads, but don't need to maintain a pool, just directly use threads.

To rewrite the above program directly using java.lang.Thread:
public class ThreadTest {
    public static void main(String args[]) throws InterruptedException {
        Thread t1 = new Thread(new Task("## running task 1..."));
        Thread t2 = new Thread(new Task("## running task 2..."));
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("## after tasks, in main");
    }

    private static class Task implements Runnable {
        private String msg;

        private Task(String s) {
            this.msg = s;
        }

        @Override public void run() {
            System.out.println(msg);
        }
    }
}
The 2 join calls are added to wait for the 2 child threads to finish processing before proceeding to the main thread execution.

10 comments:

Pavel Savinov said...

We can execute es.shutdown(); after the second task added. It will finish all the tasks and shutdown the thread pool. It will help on Windows with any kind of ThreadPool.

javahowto said...

Tasks may not have completed when calling shutdown() after the second task is added.

From ThreadPool javadoc:

public void shutdown()

Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted. Invocation has no additional effect if already shut down.

This method does not wait for previously submitted tasks to complete execution. Use awaitTermination to do that.

Pavel Savinov said...

if (es.isTerminated()) es.shutdown();

Could it be the right solution?

javahowto said...

From javadoc, isTerminated returns false before calling shutdown, so

if (es.isTerminated()) es.shutdown();

will eval to false and skip shutdown.

Thinking more about shutdown(), it is indeed an orderly shutdown. It will tell the JVM to wait for all threads in the pool to finish their task before exiting JVM. However, the shutdown() call is not blocking, meaning it will immediately proceed to the next statement in the main thread. So next couple of lines in the main thread may run before previously some lines in submitted tasks.

If you want to the main thread to wait at shutdown() point for all tasks to complete, then add this statement after shutdown():

es.awaitTermination(10, TimeUnit.SECONDS);

apkany said...

if (es.isTerminated()) es.shutdown();

Could it be the right solution?

Hariharan s said...

Hi,I have studied Java Applicatioin Process Hangs on Windows and Cached Thread Pool.Thanks...

Theosoft

Oliver said...

Hi, I did not find any other means to contact you, so I will just comment here:

I recently translated my German C# blog (http://csharp-tricks.blogspot.com/) into English and am now looking for some link partners.
The English version is pretty new but a complete translation of the German version, which I would consider quite succesful, for example is my C# blog ranked 1st in Google when searching "C# Blog" (German).
Therefor, I expect the English page to become also that or even more known soon.
I would be really happy if you considered such a link exchange.

Hope to hear from you

Oliver said...

Hi, I did not find any other means to contact you, so I just comment here:

I recently translated my German C# blog (http://csharp-tricks.blogspot.com/) into English and am now looking for some link partners.
The English version is pretty new but a complete translation of the German version, which I would consider quite succesful, for example is my C# blog ranked 1st in Google when searching "C# Blog" (German).
Therefor, I expect the English page to become also that or even more known soon.
I would be really happy if you considered such a link exchange.

Hope to hear from you

Oliver said...

Hi, I did not find any other means to contact you, so I just comment here:

I recently translated my German C# blog (http://csharp-tricks.blogspot.com/) into English and am now looking for some link partners.
The English version is pretty new but a complete translation of the German version, which I would consider quite succesful, for example is my C# blog ranked 1st in Google when searching "C# Blog" (German).
Therefor, I expect the English page to become also that or even more known soon.
I would be really happy if you considered such a link exchange.

Hope to hear from you

Anna said...

Great and Useful Article.

Online Java Training

Java Online Training India

Java Online Course

Java EE course

Java EE training

Best Recommended books for Spring framework

Java Interview Questions








Java Course in Chennai

Java Online Training India