干净的停止tomcat/java应用程序

通常在使用了jdbc或者netty的应用程序中,当shutdown tomcat或java应用程序时,会出现无法停止的情况,报类似如下错误:

严重: The web application [] registered the JDBC driver [com.mysql.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.
2014-12-11 17:57:37 org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
严重: The web application [] appears to have started a thread named [FileWatchdog] but has failed to stop it. This is very likely to create a memory leak.
2014-12-11 17:57:37 org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
严重: The web application [] appears to have started a thread named [DefaultQuartzScheduler_Worker-1] but has failed to stop it. This is very likely to create a memory leak.
2014-12-11 17:57:37 org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
严重: The web application [] appears to have started a thread named [DefaultQuartzScheduler_Worker-2] but has failed to stop it. This is very likely to create a memory leak.
2014-12-11 17:57:37 org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
严重: The web application [] appears to have started a thread named [DefaultQuartzScheduler_Worker-3] but has failed to stop it. This is very likely to create a memory leak.
2014-12-11 17:57:37 org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
严重: The web application [] appears to have started a thread named [DefaultQuartzScheduler_Worker-4] but has failed to stop it. This is very likely to create a memory leak.

应该来说,在至少95%的应用中,这个时候采用kill -9杀掉tomcat或java进程的方式并没有什么问题,因为通常业务进行了检测,亦或是互联网应用可以容忍少量的数据不一致性。所以这个时候无需强行介入或者关心,但是如果应用中缓存了相关需要持久化的数据,这个时候就不能直接这么粗暴的停止应用程序了,这会导致数据的不一致性。

在我们公司开发的通信应用框架中,其中支持一种可信的模式,所有请求接收到必须被执行,因此为了最大化性能,我们使用了进程内缓存,同时为了确保尽可能最小化不必要的异常检测和客户体验,我们需要干净的关闭相关线程池和连接。

在运行于servlet容器的模式下,可通过实现ServletContextListener接口或者继承spring的ContextLoaderListener类的contextDestroyed方法来释放相关资源,如下所示:

    public void contextDestroyed(ServletContextEvent event) {
        SpiderShutdownCleaner.shutdown();
        super.contextDestroyed(event);
    }

当所有的非daemon线程均停止后,对于活动线程而言,其实就是调用Thread.interupt(正常停止)或者stop(强杀,已经deprecated),jvm就会退出。