5

I have a ExecutorService executor = Executors.newSingleThreadExecutor(); that i want to stop when the server is shutting down.

I have a class that implements ServletContextListener and it's annotated with @WebListener.

I have the two methods in that class:

@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
    System.out.println("ServletContextListener started");
}

@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
    executor.shutdown();
    executor.shutdownNow();
    System.out.println("ServletContextListener destroyed");
}

And I see that it prints what's in both of them when it's supposed to, but when I press the stop button once in intelij, I get:

SEVERE: The web application [] appears to have started a thread named [pool-2-thread-1] but has failed to stop it. This is very likely to create a memory leak.

Right after it printed ServletContextListener destroyed.

I need to press the stop button again to fully stop it.

Why it doesn't shutdown the ExecutorService even though it reached the executor.shutdown();? What am I doing wrong?

PS: this is the only ExecutorService I have and no other threads are made by me.

EDIT2:

The executor service is a field in a singleton class, it's initialized with the class:

private ExecutorService executor = Executors.newSingleThreadExecutor();

This is how the class is initialized (lazy initialization):

public static RoomsManager getRoomsManager(ServletContext servletContext) {
    if (servletContext.getAttribute(MANAGER_GAMES_ATTRIBUTE_NAME) == null) {
        servletContext.setAttribute(MANAGER_GAMES_ATTRIBUTE_NAME, new RoomsManager());
    }
    return (RoomsManager)servletContext.getAttribute(MANAGER_GAMES_ATTRIBUTE_NAME);
}

And is annotated like this:

@WebListener
public class RoomsManager  implements ServletContextListener {

The stop button is the red square near the play and debug buttons in intelij IDEA.

shinzou
  • 5,850
  • 10
  • 60
  • 124
  • Can you implement shutdown on these lines? http://stackoverflow.com/questions/36644043/how-to-forcefully-shutdown-java-executorservice/36644320#36644320 – Ravindra babu Oct 25 '16 at 10:25
  • @Ravindrababu I copied it into `contextDestroyed()`, it didn't change anything. It doesn't print the `("Pool did not terminate");` though. I Also tried 1 sec instead of 60. – shinzou Oct 25 '16 at 10:31
  • change if (!pool.awaitTermination(60, TimeUnit.SECONDS)) condition to while condition and sleep for 60 seconds. while i(!pool.awaitTermination(60, TimeUnit.SECONDS)){ Thread.sleep(1000);} – Ravindra babu Oct 25 '16 at 10:33
  • @Ravindrababu still the same, I even waited more than a minute. BTW, the executor already finished the tasks it was given and doesn't receive any more by the time I check. – shinzou Oct 25 '16 at 10:41
  • Create a ThreadFactroy, to name the threads within your pool. That way you can see if the remaining pool is yours, or if there are more. – Stefan Oct 25 '16 at 10:58
  • Looks like you have long running tasks. Other way is : Store Future objects in list on submission to ExecutorService, Iterate through all of them by using get() method. Once you are done with iteration of fall tasks, you can shutdown – Ravindra babu Oct 25 '16 at 11:58
  • @Stefan I changed it to be like this: http://stackoverflow.com/a/6113794/4279201 and now it shuts down properly, what the heck? (to be like this: `private ExecutorService executor = Executors.newSingleThreadExecutor(new YourThreadFactory());`) – shinzou Oct 25 '16 at 12:46
  • @Stefan I tried to do `Thread.currentThread().setName("FooName");` in the method that the executor runs, and when I close the server `FooName` shows up. – shinzou Oct 25 '16 at 12:55
  • Edit your Question to explain where you launch the executor, and show the code, noting where the `executor` variable lives. I was surprised to not see that code in your `contextInitialized` method. I launch and shutdown multiple executors in my `@WebListener` in the `contextInitialized` and `contextDestroyed` methods respectively without a problem in Tomcat. Also, explain what “stop” button you are pressing. – Basil Bourque Oct 28 '16 at 15:56
  • @BasilBourque edited. – shinzou Oct 28 '16 at 16:03
  • @kuhaku So how does your `@WebListener` have access to a private variable `executor` on another class? I suspect the problem lies in this complicated arrangement of yours, possibly overly-complicated. – Basil Bourque Oct 28 '16 at 16:08
  • The class that has the executor (`RoomsManager`) is annotated with `@WebListener`. I added how now. – shinzou Oct 28 '16 at 16:11

2 Answers2

2

The problem is that you have two different RoomsManager instances (and hence, two different executors): first is created by Tomcat, and second is created by you.

When you annotate RoomsManager with @WebListener, Tomcat automatically creates an instance of that class and subscribes it to receive servlet context create/destroy events. That instance is the one that actually stops its executor and prints ServletContextListener destroyed.

The second instance is created by you in the getRoomsManager method (by the way, that method doesn't look thread-safe). That instance is not registered with Tomcat and doesn't receive servlet context "destroy" event, so it doesn't even try to shutdown its executor.

Roman
  • 6,486
  • 2
  • 23
  • 41
  • How can I get the instance that tomcat creates? And you're right it isn't thread safe now that I look at it. – shinzou Oct 28 '16 at 21:32
  • @kuhaku For example, `RoomsManager` can do `servletContextEvent.getServletContext().setAttribute(..., this)` inside its `contextInitialized()` method, so you'll be able to call `getAttribute(...)` to get it. – Roman Oct 28 '16 at 21:44
  • Thanks that did it. They really should improve their documentation, I didn't see anywhere it mentioned that tomcat creates an instance on its own. https://tomcat.apache.org/tomcat-7.0-doc/servletapi/javax/servlet/annotation/WebListener.html – shinzou Oct 29 '16 at 10:36
  • @kuhaku It's kinda implied, since that was the way it worked with `web.xml`. And web annotations are really an extension of `web.xml`. – Roman Oct 29 '16 at 14:46
1

Doing this worked:

class YourThreadFactory implements ThreadFactory {
    public Thread newThread(Runnable r) {
        return new Thread(r, "Your name");
    }
}
private ExecutorService executor = Executors.newSingleThreadExecutor(new YourThreadFactory());

Because apparently, the threads of tomcat are daemons, and therefore, when they create a new thread with return new Thread(r, "Your name"); it also becomes a daemon.

But in the DefaultThreadFactory that an executor service use, I saw that it makes sure daemonity of new threads is off.

That doesn't explain why executor.shutdown(); didn't work though, but now at least it properly shuts down.

shinzou
  • 5,850
  • 10
  • 60
  • 124