2015-12-09 36 views
1

当从Tomcat中取消部署应用程序时,线程仍处于打开状态。Tomcat + TomcatJDBC ServletContextListener打开线程

org.apache.catalina.loader.WebappClassLoader clearReferencesThreads 
SEVERE: The web application [/services] appears to have started a thread named [Abandoned connection cleanup thread] but has failed to stop it. This is very likely to create a memory leak. 
org.apache.catalina.loader.WebappClassLoader clearReferencesThreads 
SEVERE: The web application [/services] appears to have started a thread named [Timer-0] but has failed to stop it. This is very likely to create a memory leak. 

应用程序维护地图数据源并运行ScheduledExecutorService的更新地图每5分钟。使用TomcatJDBC以下参数创建

@WebListener 
public class DataSourceFactory implements ServletContextListener 
{ 
    private static Map<String, DataSource> rdsDataSourceMap; 
    private static ScheduledExecutorService scheduler; 
    private static final long CONNECTION_MAP_REFRESH_INTERVAL = 5; 

    @Override 
    public void contextInitialized(ServletContextEvent event) 
    { 
     scheduler = Executors.newSingleThreadScheduledExecutor(); 
     scheduler.scheduleAtFixedRate(new Runnable(){ 
      @Override 
      public void run() { 
       cacheDatasourceMap(); 
      } 
     }, 0, CONNECTION_MAP_REFRESH_INTERVAL, TimeUnit.MINUTES); 
    } 

    @Override 
    public void contextDestroyed(ServletContextEvent event) 
    { 
     scheduler.shutdownNow(); 
     if (localPool != null) { 
      localPool.close(); 
     } 
     for (DataSource ds : rdsDataSourceMap.values()) { 
      if (ds != null) { 
       ds.close(); 
      } 
     } 
    } 

    private void cacheDatasourceMap() 
    { 
     ... 
    } 

    .... 
} 

的数据源:

driver=com.mysql.jdbc.Driver 
jmxEnabled=true 
testWhileIdle=true 
testOnBorrow=true 
validationQuery=SELECT 1 
testOnReturn=false 
validationInterval=30000 
timeBetweenEvictionRunsMillis=5000 
maxActive=100 
maxIdle=20 
initialSize=10 
maxWait=100000 
removeAbandonedTimeout=60 
minEvictableIdleTimeMillis=30000 
minIdle=10 
logAbandoned=true 
removeAbandoned=true 
jdbcInterceptors=org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer;org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer;org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReportJmx(threshold=10000) 

UPDATE

摆脱ScheduledExecutorService的我仍然看到了计时器线程后保持打开。我在contextDestroyed()的末尾添加了一条日志语句,并验证它正在通过关闭DataSources。

我也证实了Tomcat的lib中的MySQL驱动程序并没有在WAR中。

回答

0

首先,Tomcat没有办法做到这一点,您正在使用Java SE创建一个Executor。因此,应用程序服务器(Java EE)不能也不应该管理您直接从Java SE创建的此ExecutorService。如果您想使用Java EE ExecutorService,请考虑使用ManagedScheduledExecutorService,因为它使用应用程序服务器的线程池,所以您不必担心关闭。随着这一点,在问题...

您正在使用shutdownNow()这是一个“快速和肮脏”的方式关闭ExecutorService。如果你想轻轻推动你的应用程序,我会建议使用ExecutorService.shutdown()结合ExecutorService.awaitTermination()来代替。

根据文档,shutdownNow()不能保证什么可以实际停止。

此方法不会等待主动执行的任务终止。
...
除尽力尝试之外,没有任何保证可停止处理主动执行的任务。

如果您在意等待任务停止,您需要使用awaitTermination()shutdown()shutdownNow()唯一可以做的就是调用interrupt(),这可能会或可能不会实际阻止线程。要等待终止,请执行以下操作:

executor.shutdown(); // or shutdownNow() 
if (!executor.isTerminated()) 
    executor.awaitTermination(10, TimeUnit.SECONDS); // wait for up to 10s