Search code examples
javamultithreadingtomcat

Java: How to stop threads properly when Tomcat is going to be restarted


My problem is: each time when I restart my Tomcat as a service (via services.msc in Windows) that process take a long time (about 2 minutes). In Tomcat logs (tomcat9-stderr.yyyy-mm-dd.txt) I see that all threads could not stop properly and, additionaly, there is the issue with unregistration of the JDBC driver:

26-Jul-2024 15:32:16.575 WARNING [Thread-9] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesJdbc The web application [MyWebApp] registered the JDBC driver [net.sourceforge.jtds.jdbc.Driver] but failed to unregister it when the web application was stopped. To prevent a memory leak, the JDBC Driver has been forcibly unregistered.
26-Jul-2024 15:32:16.575 WARNING [Thread-9] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [MyWebApp] appears to have started a thread named [Timer-0] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
 java.lang.Object.wait(Native Method)
 java.lang.Object.wait(Unknown Source)
 java.util.TimerThread.mainLoop(Unknown Source)
 java.util.TimerThread.run(Unknown Source)

The same situation is with each separate thread. So, there are two questions:

  1. What is the proper way to unregister the JDBC driver?
  2. Is it possible that errors with thread closing are a reason of slow Tomcat restart? If it is, what is the proper way to close each thread?

I tried to insert the next code inside each thread, but the same exceptions are observed at each restart:

if (Thread.currentThread().isInterrupted())
     {
         throw new InterruptedException();
     }

I use Java 8 and Tomcat-9.

I greatly appreciate any help! Thanks in advance!


Solution

  • tl;dr

    No, your own threads are not cleaned up for you, unless you use Jakarta Concurrency.

    Clean up your own threads

    If you start any executor services, threads, or timers from your Servlet, then you must close those before your web app exits. If you neglect to do this, the backing threads may continue running indefinitely like a zombie 🧟‍♂️.

    Hook into web app lifecycle

    You can run code when your web app, your “context”, starts and ends. Write a class that implements ServletContextListener, and annotate with @WebListener.

    Avoid Timer/TimerTask

    Beware: Timer and TimerTask were long ago supplanted by the Executors framework added to Java 5, as noted in the Javadoc.

    Jakarta Concurrency

    The best solution is to migrate to a Servlet container that supports Jakarta Concurrency rather than open your own executor services, threads, or timers. Such a Servlet container will automatically clean up any threads managed on your behalf.

    You have a choice of many such containers including some editions of Apache TomEE (built on top of Tomcat).


    Search to learn more. These topics have been covered many times already on Stack Overflow.