【问题标题】:What is the proper way to shutdown threads when tomcat closes?当tomcat关闭时关闭线程的正确方法是什么?
【发布时间】:2012-04-12 20:09:35
【问题描述】:

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

信息:非法访问:此 Web 应用程序实例已停止
已经。无法加载 org.apache.log4j.helpers.NullEnumeration。
最终的以下堆栈跟踪是由
引发的错误引起的 调试目的以及尝试终止线程
造成非法访问,对功能没有影响。可投掷
发生: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。最终
以下堆栈跟踪是由调试引发的错误引起的
目的以及尝试终止导致
的线程 非法访问,对功能没有影响。发生了 Throwable:
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

我正在做的是在ServletContextListenercontextDestroyed 我添加了关闭挂钩如下:

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

更新 2:
按照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。
最终的后续堆栈跟踪是由
引发的错误引起的 调试目的以及尝试终止线程
造成非法访问,对功能没有影响。可投掷
发生:java.lang.IllegalStateException
在 org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1587)
在 org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1546)

为什么?这些类是否已经被垃圾回收了?

【问题讨论】:

    标签: multithreading tomcat web-applications servlets log4j


    【解决方案1】:

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

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

    编辑

    监听器的contextDestroyed() 执行器服务迟到了。正如 javadoc 中所述,所有 servlet 和过滤器都将被销毁 之前 任何 ServletContextListener 都会收到上下文销毁的通知。

    而根据 javadoc 覆盖 servlet destroy() 应该没问题 此方法使 servlet 有机会清理所持有的任何资源(例如,内存、文件句柄, 线程...

    @Override
    public void destroy(  ) {
    
    
            myThreadExecutor.shutdown();
    
            super.destroy(  );
    }
    

    【讨论】:

    • 我想使用关闭钩子,这样我确信它们会在 JVM 关闭时被调用
    • 相信你的容器:-)。无论如何,请尽早注册;我会选择 servlet init() 方法。
    • Jim,你能不能直接在 Servlet 的 destroy() 方法中调用SomeClass.updater.shutdown();(而不是在“为时已晚”的监听器中)。根据 destroy() 的 javadoc 此方法使 servlet 有机会清理正在持有的任何资源(例如,内存、文件句柄、线程)并确保任何持久状态与 servlet 的当前状态同步内存中的状态。
    • 据我所知,destroy() 不能保证执行完成。即Tomcat将调用destroy(),然后在终止后不久,不考虑destroy()是否完成。
    【解决方案2】:

    打电话

    LogManager.shutdown();
    

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

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

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

    【讨论】:

      猜你喜欢
      • 2012-04-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-14
      • 1970-01-01
      • 2013-07-26
      • 2018-06-05
      相关资源
      最近更新 更多