Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ExecutorService doesn't shut down from contextDestroyed() when stopping Tomcat

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.

like image 991
shinzou Avatar asked Oct 18 '22 23:10

shinzou


2 Answers

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.

like image 182
Roman Avatar answered Oct 21 '22 05:10

Roman


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.

like image 25
shinzou Avatar answered Oct 21 '22 06:10

shinzou