2015-12-09 64 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