Toni
Toni

Reputation: 165

stopping ExecutorService Threads in Tomcat

I am developing an app which uses Tomcat to communicate with a Raspberry PI. The GUI is done in html and communication between these two is achieved using websockets. So far so good.

I want to start a thread when the app first run so it starts a series of tasks. This is a test I've done:

    public class ContexService implements ServletContextListener {

    Thread thread;
    ExecutorService executorService;

//    ServiceManager serviceManager;
    @Override
    public void contextInitialized(ServletContextEvent sce) {

        System.out.println("-------------> CONTEXT INITIALIZED <-------------");
        executorService = Executors.newSingleThreadExecutor();
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                          Test p = new Test();
                int count = 0;
                while (count < 20) {
                    p.imprimir(count);
                    count++;
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException ex) {
                       System.out.println("error - > " + ex.getMessage());
                    }
            }
        }});

}

    @Override
    public void contextDestroyed(ServletContextEvent sce) {

        System.out.println("-------------> CONTEXT DESTROYED <-------------");
        if(executorService!=null)
        {
            if(!executorService.isShutdown())
            {
                try {
                    executorService.shutdown();
                    executorService.awaitTermination(3, TimeUnit.SECONDS);
                    if(!executorService.isTerminated())
                    {
                        executorService.shutdownNow();
                    }
                } catch (InterruptedException ex) {
                    Logger.getLogger(ContexService.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }

    }
}

I run this and it works. My problem is that every time I press "play" on Netbeans it starts everything again ( which is what I want ) but the thread launched using ExecutorService remaing active. I attach the tomcat's console output so you see what I mean:

-------------> CONTEXT DESTROYED <-------------
05-May-2016 15:19:41.668 INFO [http-apr-8080-exec-66] org.apache.catalina.startup.HostConfig.undeploy Repliegue (undeploy) de la aplicación web que tiene como trayectoria de contexto /safemo
05-May-2016 15:19:41.694 INFO [http-apr-8080-exec-65] org.apache.catalina.startup.HostConfig.deployDescriptor Desplieque del descriptor de configuración C:\apache-tomcat-8.0.33\conf\Catalina\localhost\safemo.xml
05-May-2016 15:19:42.268 INFO [http-apr-8080-exec-65] org.apache.catalina.startup.HostConfig.deployDescriptor Deployment of configuration descriptor C:\apache-tomcat-8.0.33\conf\Catalina\localhost\safemo.xml has finished in 574 ms
05-May-2016 15:19:42.273 INFO [http-apr-8080-exec-69] org.apache.catalina.util.LifecycleBase.start The start() method was called on component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/safemo]] after start() had already been called. The second call will be ignored.
-------------> CONTEXT INITIALIZED <-------------
15:19:42.446 [pool-4-thread-1] DEBUG sal.Test - 0 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:19:43.455 [pool-4-thread-1] DEBUG sal.Test - 1 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:19:44.456 [pool-4-thread-1] DEBUG sal.Test - 2 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:19:45.456 [pool-4-thread-1] DEBUG sal.Test - 3 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:19:46.456 [pool-4-thread-1] DEBUG sal.Test - 4 --> TEST <--Thu May 05 15:19:42 CEST 2016
-------------> CONTEXT DESTROYED <-------------
15:19:47.456 [pool-4-thread-1] DEBUG sal.Test - 5 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:19:48.456 [pool-4-thread-1] DEBUG sal.Test - 6 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:19:49.457 [pool-4-thread-1] DEBUG sal.Test - 7 --> TEST <--Thu May 05 15:19:42 CEST 2016
error - > sleep interrupted
15:19:49.484 [pool-4-thread-1] DEBUG sal.Test - 8 --> TEST <--Thu May 05 15:19:42 CEST 2016
05-May-2016 15:19:49.487 WARNING [http-apr-8080-exec-77] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [safemo] appears to have started a thread named [pool-4-thread-1] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
 java.lang.Thread.sleep(Native Method)
 sal.ContexService$1.run(ContexService.java:42)
 java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
 java.util.concurrent.FutureTask.run(FutureTask.java:266)
 java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
 java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
 java.lang.Thread.run(Thread.java:745)
05-May-2016 15:19:49.991 INFO [http-apr-8080-exec-77] org.apache.catalina.startup.HostConfig.undeploy Repliegue (undeploy) de la aplicación web que tiene como trayectoria de contexto /safemo
05-May-2016 15:19:50.034 INFO [http-apr-8080-exec-63] org.apache.catalina.startup.HostConfig.deployDescriptor Desplieque del descriptor de configuración C:\apache-tomcat-8.0.33\conf\Catalina\localhost\safemo.xml
15:19:50.484 [pool-4-thread-1] DEBUG sal.Test - 9 --> TEST <--Thu May 05 15:19:42 CEST 2016
05-May-2016 15:19:50.682 INFO [http-apr-8080-exec-63] org.apache.catalina.startup.HostConfig.deployDescriptor Deployment of configuration descriptor C:\apache-tomcat-8.0.33\conf\Catalina\localhost\safemo.xml has finished in 648 ms
05-May-2016 15:19:50.686 INFO [http-apr-8080-exec-70] org.apache.catalina.util.LifecycleBase.start The start() method was called on component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/safemo]] after start() had already been called. The second call will be ignored.
-------------> CONTEXT INITIALIZED <-------------
15:19:50.818 [pool-5-thread-1] DEBUG sal.Test - 0 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:19:51.484 [pool-4-thread-1] DEBUG sal.Test - 10 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:19:51.824 [pool-5-thread-1] DEBUG sal.Test - 1 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:19:52.485 [pool-4-thread-1] DEBUG sal.Test - 11 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:19:52.825 [pool-5-thread-1] DEBUG sal.Test - 2 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:19:53.485 [pool-4-thread-1] DEBUG sal.Test - 12 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:19:53.825 [pool-5-thread-1] DEBUG sal.Test - 3 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:19:54.485 [pool-4-thread-1] DEBUG sal.Test - 13 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:19:54.825 [pool-5-thread-1] DEBUG sal.Test - 4 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:19:55.486 [pool-4-thread-1] DEBUG sal.Test - 14 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:19:55.826 [pool-5-thread-1] DEBUG sal.Test - 5 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:19:56.486 [pool-4-thread-1] DEBUG sal.Test - 15 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:19:56.826 [pool-5-thread-1] DEBUG sal.Test - 6 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:19:57.486 [pool-4-thread-1] DEBUG sal.Test - 16 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:19:57.826 [pool-5-thread-1] DEBUG sal.Test - 7 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:19:58.486 [pool-4-thread-1] DEBUG sal.Test - 17 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:19:58.826 [pool-5-thread-1] DEBUG sal.Test - 8 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:19:59.486 [pool-4-thread-1] DEBUG sal.Test - 18 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:19:59.826 [pool-5-thread-1] DEBUG sal.Test - 9 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:20:00.486 [pool-4-thread-1] DEBUG sal.Test - 19 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:20:00.827 [pool-5-thread-1] DEBUG sal.Test - 10 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:20:01.827 [pool-5-thread-1] DEBUG sal.Test - 11 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:20:02.827 [pool-5-thread-1] DEBUG sal.Test - 12 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:20:03.827 [pool-5-thread-1] DEBUG sal.Test - 13 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:20:04.827 [pool-5-thread-1] DEBUG sal.Test - 14 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:20:05.827 [pool-5-thread-1] DEBUG sal.Test - 15 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:20:06.827 [pool-5-thread-1] DEBUG sal.Test - 16 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:20:07.828 [pool-5-thread-1] DEBUG sal.Test - 17 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:20:08.828 [pool-5-thread-1] DEBUG sal.Test - 18 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:20:09.828 [pool-5-thread-1] DEBUG sal.Test - 19 --> TEST <--Thu May 05 15:19:50 CEST 2016

I launch the app and starts printing test using pool4-thread 1. I relaunch it and the thread remain working, and a new thread is started ( pool5-thread1 ).

Why is the thread not stopping if I perform "executorService.shutDown" on contextDestroyed?

Thank you!

Upvotes: 3

Views: 4009

Answers (2)

Ravindra babu
Ravindra babu

Reputation: 38950

Why is the thread not stopping if I perform "executorService.shutDown" on contextDestroyed?

Shutdown ExecutorService as recommended by Oracle documentation page.

Your method will should change

public void contextDestroyed(ServletContextEvent sce) {  

     executorService.shutdown(); // Disable new tasks from being submitted
       try {
         // Wait a while for existing tasks to terminate
         if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
           executorService.shutdownNow(); // Cancel currently executing tasks
           // Wait a while for tasks to respond to being cancelled
           if (!executorService.awaitTermination(60, TimeUnit.SECONDS))
               System.err.println("executorService did not terminate");
         }
       } catch (InterruptedException ie) {
         // (Re-)Cancel if current thread also interrupted
         executorService.shutdownNow();
         // Preserve interrupt status
         Thread.currentThread().interrupt();
       }
}

shutdown(): Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted.

shutdownNow():Attempts to stop all actively executing tasks, halts the processing of waiting tasks, and returns a list of the tasks that were awaiting execution.

You can change this condition as below in case of longer duration for shutting down ExecutorService

From

if (!executorService.awaitTermination(60, TimeUnit.SECONDS))

To

while (!executorService.awaitTermination(60, TimeUnit.SECONDS))
Thread.sleep(60000);

If you handle shutdown properly as per above code, you may not get second issue at multiple ExecutorService pools.

Upvotes: 1

Pierre
Pierre

Reputation: 2912

It seems like there are two mysteries to solve in your question:

MYSTERY1: Why won't it stop? Check your run method: it will loop 20 times (from 0 to 19) and wait 1 second each time it loops. So, it won't be done until at least TWENTY seconds.

Your shutdown only waits THREE seconds. By that time, your work is not done. You could try to replace this line:

executorService.awaitTermination(3, TimeUnit.SECONDS);

with this:

executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);

That way, it will wait until your task is truly done. NOTE: even if you didn't wait and called shutdownNow right from the get go, that would not work either. That part of the code should be refactored so that you don't simply loop 20 times but also check if the thread is interrupted while it loops so that it can cleanly complete (short circuit) its processing. Try to change this line:

while (count<20) {

with this one:

while (count<20 && !Thread.currentThread().isInterrupted()) {

This solution should help explain what I said above: Why ExecutorService.shutdownNow method can't stop the thread Also worth reading the docs for shutdownNow: http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ExecutorService.html

There are no guarantees beyond best-effort attempts to stop processing actively executing tasks. For example, typical implementations will cancel via Thread.interrupt(), so any task that fails to respond to interrupts may never terminate.

MYSTERY2: Why does Netbeans have the 2 threads running at the same time? This is probably because your running thread has never stopped processing and is not handling the interruption requests coming its way. So, this is all expected, your program will continue to run until it completes (java is trying to tell your code to stop but your code is not currently listening to those messages). So, yes, both threads are running at the same time when you start the program again (the previous run is still running and you're starting another one). Once a thread reaches the 20th loop (index=19), taht thread gets cleaned up and that JVM hopefully finally shuts down. That is, unless you run into the NetBeans bug where it leaves JVMs running: https://netbeans.org/bugzilla/show_bug.cgi?id=232322

Upvotes: 2

Related Questions