停止Tomcat中的ExecutorService线程

时间:2016-05-05 13:31:44

标签: java multithreading tomcat threadpool executorservice

我正在开发一个使用Tomcat与Raspberry PI进行通信的应用程序。 GUI以html完成,并且使用websockets实现这两者之间的通信。到现在为止还挺好。

我想在应用程序首次运行时启动一个线程,以便启动一系列任务。这是我已经完成的测试:

    public class ContexService implements ServletContextListener {

    Thread thread;
    ExecutorService executorService;

//    ServiceManager serviceManager;
    @Override
    public void contextInitialized(ServletContextEvent sce) {

        System.out.println("-------------> CONTEXT INITIALIZED <-------------");
        executorService = Executors.newSingleThreadExecutor();
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                          Test p = new Test();
                int count = 0;
                while (count < 20) {
                    p.imprimir(count);
                    count++;
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException ex) {
                       System.out.println("error - > " + ex.getMessage());
                    }
            }
        }});

}

    @Override
    public void contextDestroyed(ServletContextEvent sce) {

        System.out.println("-------------> CONTEXT DESTROYED <-------------");
        if(executorService!=null)
        {
            if(!executorService.isShutdown())
            {
                try {
                    executorService.shutdown();
                    executorService.awaitTermination(3, TimeUnit.SECONDS);
                    if(!executorService.isTerminated())
                    {
                        executorService.shutdownNow();
                    }
                } catch (InterruptedException ex) {
                    Logger.getLogger(ContexService.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }

    }
}

我运行它并且它有效。我的问题是,每次按&#34;播放&#34;在Netbeans上,它再次启动所有内容(这就是我想要的)但是使用ExecutorService重新启动的线程是活动的。我附上了tomcat的控制台输出,所以你明白了我的意思:

-------------> CONTEXT DESTROYED <-------------
05-May-2016 15:19:41.668 INFO [http-apr-8080-exec-66] org.apache.catalina.startup.HostConfig.undeploy Repliegue (undeploy) de la aplicación web que tiene como trayectoria de contexto /safemo
05-May-2016 15:19:41.694 INFO [http-apr-8080-exec-65] org.apache.catalina.startup.HostConfig.deployDescriptor Desplieque del descriptor de configuración C:\apache-tomcat-8.0.33\conf\Catalina\localhost\safemo.xml
05-May-2016 15:19:42.268 INFO [http-apr-8080-exec-65] org.apache.catalina.startup.HostConfig.deployDescriptor Deployment of configuration descriptor C:\apache-tomcat-8.0.33\conf\Catalina\localhost\safemo.xml has finished in 574 ms
05-May-2016 15:19:42.273 INFO [http-apr-8080-exec-69] org.apache.catalina.util.LifecycleBase.start The start() method was called on component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/safemo]] after start() had already been called. The second call will be ignored.
-------------> CONTEXT INITIALIZED <-------------
15:19:42.446 [pool-4-thread-1] DEBUG sal.Test - 0 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:19:43.455 [pool-4-thread-1] DEBUG sal.Test - 1 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:19:44.456 [pool-4-thread-1] DEBUG sal.Test - 2 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:19:45.456 [pool-4-thread-1] DEBUG sal.Test - 3 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:19:46.456 [pool-4-thread-1] DEBUG sal.Test - 4 --> TEST <--Thu May 05 15:19:42 CEST 2016
-------------> CONTEXT DESTROYED <-------------
15:19:47.456 [pool-4-thread-1] DEBUG sal.Test - 5 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:19:48.456 [pool-4-thread-1] DEBUG sal.Test - 6 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:19:49.457 [pool-4-thread-1] DEBUG sal.Test - 7 --> TEST <--Thu May 05 15:19:42 CEST 2016
error - > sleep interrupted
15:19:49.484 [pool-4-thread-1] DEBUG sal.Test - 8 --> TEST <--Thu May 05 15:19:42 CEST 2016
05-May-2016 15:19:49.487 WARNING [http-apr-8080-exec-77] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [safemo] appears to have started a thread named [pool-4-thread-1] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
 java.lang.Thread.sleep(Native Method)
 sal.ContexService$1.run(ContexService.java:42)
 java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
 java.util.concurrent.FutureTask.run(FutureTask.java:266)
 java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
 java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
 java.lang.Thread.run(Thread.java:745)
05-May-2016 15:19:49.991 INFO [http-apr-8080-exec-77] org.apache.catalina.startup.HostConfig.undeploy Repliegue (undeploy) de la aplicación web que tiene como trayectoria de contexto /safemo
05-May-2016 15:19:50.034 INFO [http-apr-8080-exec-63] org.apache.catalina.startup.HostConfig.deployDescriptor Desplieque del descriptor de configuración C:\apache-tomcat-8.0.33\conf\Catalina\localhost\safemo.xml
15:19:50.484 [pool-4-thread-1] DEBUG sal.Test - 9 --> TEST <--Thu May 05 15:19:42 CEST 2016
05-May-2016 15:19:50.682 INFO [http-apr-8080-exec-63] org.apache.catalina.startup.HostConfig.deployDescriptor Deployment of configuration descriptor C:\apache-tomcat-8.0.33\conf\Catalina\localhost\safemo.xml has finished in 648 ms
05-May-2016 15:19:50.686 INFO [http-apr-8080-exec-70] org.apache.catalina.util.LifecycleBase.start The start() method was called on component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/safemo]] after start() had already been called. The second call will be ignored.
-------------> CONTEXT INITIALIZED <-------------
15:19:50.818 [pool-5-thread-1] DEBUG sal.Test - 0 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:19:51.484 [pool-4-thread-1] DEBUG sal.Test - 10 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:19:51.824 [pool-5-thread-1] DEBUG sal.Test - 1 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:19:52.485 [pool-4-thread-1] DEBUG sal.Test - 11 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:19:52.825 [pool-5-thread-1] DEBUG sal.Test - 2 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:19:53.485 [pool-4-thread-1] DEBUG sal.Test - 12 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:19:53.825 [pool-5-thread-1] DEBUG sal.Test - 3 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:19:54.485 [pool-4-thread-1] DEBUG sal.Test - 13 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:19:54.825 [pool-5-thread-1] DEBUG sal.Test - 4 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:19:55.486 [pool-4-thread-1] DEBUG sal.Test - 14 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:19:55.826 [pool-5-thread-1] DEBUG sal.Test - 5 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:19:56.486 [pool-4-thread-1] DEBUG sal.Test - 15 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:19:56.826 [pool-5-thread-1] DEBUG sal.Test - 6 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:19:57.486 [pool-4-thread-1] DEBUG sal.Test - 16 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:19:57.826 [pool-5-thread-1] DEBUG sal.Test - 7 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:19:58.486 [pool-4-thread-1] DEBUG sal.Test - 17 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:19:58.826 [pool-5-thread-1] DEBUG sal.Test - 8 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:19:59.486 [pool-4-thread-1] DEBUG sal.Test - 18 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:19:59.826 [pool-5-thread-1] DEBUG sal.Test - 9 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:20:00.486 [pool-4-thread-1] DEBUG sal.Test - 19 --> TEST <--Thu May 05 15:19:42 CEST 2016
15:20:00.827 [pool-5-thread-1] DEBUG sal.Test - 10 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:20:01.827 [pool-5-thread-1] DEBUG sal.Test - 11 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:20:02.827 [pool-5-thread-1] DEBUG sal.Test - 12 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:20:03.827 [pool-5-thread-1] DEBUG sal.Test - 13 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:20:04.827 [pool-5-thread-1] DEBUG sal.Test - 14 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:20:05.827 [pool-5-thread-1] DEBUG sal.Test - 15 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:20:06.827 [pool-5-thread-1] DEBUG sal.Test - 16 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:20:07.828 [pool-5-thread-1] DEBUG sal.Test - 17 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:20:08.828 [pool-5-thread-1] DEBUG sal.Test - 18 --> TEST <--Thu May 05 15:19:50 CEST 2016
15:20:09.828 [pool-5-thread-1] DEBUG sal.Test - 19 --> TEST <--Thu May 05 15:19:50 CEST 2016

我启动应用程序并使用pool4-thread 1开始打印测试。我重新启动它并且线程仍在工作,并启动一个新线程(pool5-thread1)。

如果我执行&#34; executorService.shutDown&#34;为什么线程不会停止? on contextDestroyed?

谢谢!

2 个答案:

答案 0 :(得分:1)

在你的问题中似乎有两个谜团需要解决:

MYSTERY1:为什么不停止? 检查你的run方法:它将循环20次(从0到19)并在每次循环时等待1秒。所以,至少在两十秒后才能完成。

您的关机只等待三秒钟。到那时,你的工作还没有完成。您可以尝试替换此行:

executorService.awaitTermination(3, TimeUnit.SECONDS);

用这个:

executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);

这样,它会等到你的任务真正完成。注意:即使您没有等待并且从get get开始调用shutdownNow,这也无效。代码的那一部分应该被重构,这样你就不会简单地循环20次,而是检查线程在循环时是否被中断,以便它可以干净地完成(短路)它的处理。尝试更改此行:

while (count<20) {

这一个:

while (count<20 && !Thread.currentThread().isInterrupted()) {

此解决方案应该有助于解释我上面所说的内容: Why ExecutorService.shutdownNow method can't stop the thread 另外值得阅读shutdownNow:http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ExecutorService.html

的文档
  

除了尽力尝试停止处理主动执行任务之外,没有任何保证。例如,典型的实现将通过Thread.interrupt()取消,因此任何无法响应中断的任务都可能永远不会终止。

MYSTERY2:为什么Netbeans同时运行2个线程? 这可能是因为您正在运行的线程从未停止过处理,并且没有处理中断请求。所以,这是预期的,你的程序将继续运行直到它完成(java试图告诉你的代码停止但你的代码当前没有收听这些消息)。所以,是的,当你再次启动程序时,两个线程都在同一时间运行(前一个运行仍在运行,而你正在启动另一个)。一旦一个线程到达第20个循环(index = 19),taht线程就会被清除,并且JVM有望最终关闭。也就是说,除非您遇到运行JVM的NetBeans错误: https://netbeans.org/bugzilla/show_bug.cgi?id=232322

答案 1 :(得分:1)

  

如果我执行&#34; executorService.shutDown&#34;为什么线程不会停止? on contextDestroyed?

根据Oracle文档页面的建议关闭ExecutorService

你的方法应该改变

val result = (for (v<-option if condition) yield valueToResult(v)).getOrElse(defaultResult)

public void contextDestroyed(ServletContextEvent sce) { executorService.shutdown(); // Disable new tasks from being submitted try { // Wait a while for existing tasks to terminate if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) { executorService.shutdownNow(); // Cancel currently executing tasks // Wait a while for tasks to respond to being cancelled if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) System.err.println("executorService did not terminate"); } } catch (InterruptedException ie) { // (Re-)Cancel if current thread also interrupted executorService.shutdownNow(); // Preserve interrupt status Thread.currentThread().interrupt(); } } 启动有序关闭,其中先前提交的任务已执行,但不会接受任何新任务。

shutdown():尝试停止所有正在执行的任务,停止等待任务的处理,并返回等待执行的任务列表。

如果关闭shutdownNow():

的时间较长,您可以更改此条件,如下所示

ExecutorService

if (!executorService.awaitTermination(60, TimeUnit.SECONDS))

如果您按照上述代码正确处理关闭,则可能无法在多个while (!executorService.awaitTermination(60, TimeUnit.SECONDS)) Thread.sleep(60000); 池中遇到第二个问题。