当tomcat关闭时关闭线程的正确方法是什么?

时间:2012-03-29 07:35:38

标签: multithreading tomcat web-applications servlets log4j

我正在尝试在关闭Tomcat时关闭线程 具体来说,我试图关闭log4j看门狗(用于文件更改),我也试图关闭在我的网络应用程序中使用类的执行程序。
关机后,我在Catalina.out中看到例外情况 对于Log4J,我看到:

  

信息:非法访问:此Web应用程序实例已停止   已经。无法加载org.apache.log4j.helpers.NullEnumeration   最终跟随堆栈跟踪是由于抛出的错误引起的   调试目的以及尝试终止其中的线程   导致非法访问,并没有任何功能影响。 Throwable的
  发生:java.lang.IllegalStateException
          在org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1587)
          在org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1546)
          在org.apache.log4j.Category.getAllAppenders(Category.java:413)
          在org.apache.log4j.Category.closeNestedAppenders(Category.java:226)
          在org.apache.log4j.Hierarchy.shutdown(Hierarchy.java:467)
          在org.apache.log4j.LogManager.shutdown(LogManager.java:267)
          在com.listeners.myListener $ 1.run(myListener.java:232)
  线程“Thread-14”中的异常java.lang.NoClassDefFoundError:
  org.apache.log4j.helpers.NullEnumeration
          在org.apache.log4j.Category.getAllAppenders(Category.java:413)
          在org.apache.log4j.Category.closeNestedAppenders(Category.java:226)
          在org.apache.log4j.Hierarchy.shutdown(Hierarchy.java:467)
          在org.apache.log4j.LogManager.shutdown(LogManager.java:267)

对于执行者部分:

  

信息:非法访问:此Web应用程序实例已停止   已经。无法加载com.my.class.SomeClass。最终的   跟踪堆栈跟踪是由调试抛出的错误引起的   目的以及试图终止引起的线程   非法访问,并没有功能影响。发生了可怜的事:   java.lang.IllegalStateException
          在org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1587)
          在org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1546)
          在   线程“Thread-13”中的异常java.lang.NoClassDefFoundError:
  com.my.class.SomeClass

我正在ServletContextListener contextDestroyed上我正在做的事情我添加了关机挂钩,如下所示:

public void contextDestroyed(ServletContextEvent arg0) {  

         Runtime.getRuntime().addShutdownHook(new Thread(){  
            @Override  
            public void run(){  
                LogManager.shutdown();                  
            }  
         });  

    } 





 public void contextDestroyed(ServletContextEvent arg0) {  

        Runtime.getRuntime().addShutdownHook(new Thread(){  
            @Override  
            public void run(){  
                SomeClass.updater.shutdown();  
            }  
        });  

    }  

我在这里做错了什么?为什么我会得到例外?

更新
SomeClass.updaterpublic static ScheduledExecutorService LogManagerorg.apache.log4j.LogManager

UPDATE2:
在听完BGR的答案之后我直接做了

public void contextDestroyed(ServletContextEvent arg0) {  

            SomeClass.updater.shutdown();  

        }  

public void contextDestroyed(ServletContextEvent arg0) {  

                LogManager.shutdown();                  

        } 

我没有从Log4j中获得异常,但我得到SomeClass.updater public static ScheduledExecutorService的以下异常:

  

信息:非法访问:此Web应用程序实例已停止   已经。无法加载java.util.concurrent.ExecutorService。该
  最终跟踪堆栈跟踪是由于抛出的错误引起的   调试目的以及尝试终止其中的线程   导致非法访问,并没有任何功能影响。 Throwable的
  发生:java.lang.IllegalStateException
          在org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1587)
          在org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1546)

为什么呢?这些课程是否已被垃圾收集?

2 个答案:

答案 0 :(得分:3)

我会在servlet的init()方法中注册shutdown hooks而不是contextDetroyed(),但无论如何,为什么你首先需要Shutdown钩子?

你不能直接在contextDestroyed()方法中调用SomeClass.updater.shutdown();吗?

修改

监听器的

contextDestroyed()对于执行程序服务来说是迟到的。正如javadoc 中所述,所有servlet和过滤器都将被销毁 之前,任何ServletContextListeners都会被告知上下文破坏。

而覆盖servlet destroy()应该没问题,因为根据javadoc 这个方法为servlet提供了清理所有资源的机会(例如,内存,文件句柄, 线程 ...

@Override
public void destroy(  ) {


        myThreadExecutor.shutdown();

        super.destroy(  );
}

答案 1 :(得分:2)

致电

LogManager.shutdown();
contextDestroyed()方法中的

是第一步,但ExecutorService不会立即关闭。您将获得异常,因为在contextDestroyed()方法返回后,ExecutorService线程仍在运行。你需要这样做:

public void contextDestroyed(ServletContextEvent arg0) {  
    LogManager.shutdown();
    if(LogManager.awaitTermination(10, TimeUnit.SECONDS) == false) {
        LogManager.shutdownNow();
    }
} 

这样,当contextDestroyed()退出时,线程池已关闭并停止所有线程。