2017-06-16 215 views
1

我有一个Web应用程序(使用Spring /春天启动)在Tomcat 7.There运行被等被定义一些的ExecutorService:当tomcat关闭时,ExecutorService管理的线程会发生什么情况?

public static final ExecutorService TEST_SERVICE = new ThreadPoolExecutor(10, 100, 60L, 
     TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(1000), new ThreadPoolExecutor.CallerRunsPolicy()); 

的任务很重要,必须正确完成。我捕捉到的异常以及它们保存到数据库的重试,就像这样:

try { 
     ThreadPoolHolder.TEST_SERVICE.submit(new Runnable() { 
      @Override 
      public void run() { 
       try { 
        boolean isSuccess = false; 
        int tryCount = 0; 
        while (++tryCount < CAS_COUNT_LIMIT) { 
         isSuccess = doWork(param); 
         if (isSuccess) { 
          break; 
         } 
         Thread.sleep(1000); 
        } 
        if (!isSuccess) { 
         saveFail(param); 
        } 
       } catch (Exception e) { 
        log.error("test error! param : {}", param, e); 
        saveFail(param); 
       } 
      } 
     }); 
    } catch (Exception e) { 
     log.error("test error! param:{}", param, e); 
     saveFail(param); 
    } 

所以,当Tomcat关闭,将发生在池的线程是什么(正在运行或在队列中等待)?如何确保所有任务或者在关机前正确完成,或者保存到数据库重试?

回答

1

Tomcat内置了线程泄漏检测功能,所以在应用程序解除部署时应该会出现错误。作为开发人员是你的责任链接创建的Web应用程序生命周期的任何对象,这意味着你永远也不会有如果您在使用Spring引导未常数

静止状态时,你的Spring上下文已经链接到应用程序生命周期,所以最好的方法是将你的执行器创建为Spring bean,并让Spring在应用程序停止时关闭它。这里是你可以放入任何@Configuration类的例子。

@Bean(destroyMethod = "shutdownNow", name = "MyExecutorService") 
public ThreadPoolExecutor executor() { 
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 100, 60L, 
      TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(1000), 
      new ThreadPoolExecutor.CallerRunsPolicy()); 
    return threadPoolExecutor; 
} 

正如您所看到的,@Bean注释允许您指定在Spring上下文关闭时执行的destroy方法。另外我添加了name属性,这是因为Spring通常会为异步Web处理等东西创建一些ExecutorServices。当您需要使用执行程序时,只需将Autowire与其他任何spring bean一起使用即可。

@Autowired 
@Qualifier(value = "MyExecutorService") 
ThreadPoolExecutor executor; 

记住静态是EVIL,你应该只对常量和可能不可变的obbjects使用静态。

编辑

如果需要,直到任务已经处理,以阻止雄猫关机程序,你需要用一个组件执行人进行更多的控制,像这样。

@Component 
public class ExecutorWrapper implements DisposableBean { 

    private final ThreadPoolExecutor threadPoolExecutor; 

    public ExecutorWrapper() { 
     threadPoolExecutor = new ThreadPoolExecutor(10, 100, 60L, 
       TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(1000), new ThreadPoolExecutor.CallerRunsPolicy()); 
    } 

    public <T> Future<T> submit(Callable<T> task) { 
     return threadPoolExecutor.submit(task); 
    } 

    public void submit(Runnable runnable) { 
     threadPoolExecutor.submit(runnable); 
    } 

    @Override 
    public void destroy() throws Exception { 
     threadPoolExecutor.shutdown(); 
     boolean terminated = threadPoolExecutor.awaitTermination(1, TimeUnit.MINUTES); 
     if (!terminated) { 
      List<Runnable> runnables = threadPoolExecutor.shutdownNow(); 
      // log the runnables that were not executed 
     } 
    } 
} 

有了这个代码,你叫shutdown第一所以没有新的任务可提交,然后等待一些时间执行完成当前任务和队列。如果未及时完成,请致电shutdownNow中断正在运行的任务,并获取未处理的任务列表。

注意: DisposableBean有窍门,但最好的解决方案实际上是实现SmartLifecycle接口。你必须实现更多的方法,但是你可以得到更多的控制,因为直到所有的bean都被实例化并且整个bean层次结合在一起之前,线程才会被启动,它甚至允许你指定应该在哪些命令中启动组件。

+0

谢谢!它真的更优雅。但是当tomcat关闭时会发生什么?我担心未完成的任务正在运行或在队列中。如何确保在tomcat重启时完成所有任务。 – sxzhou

+0

我将编辑包含该场景的答案 –

1

作为任何Java应用程序的Tomcat将不会结束,直到所有非daeon线程都将结束。上例中的ThreadPoolExecutor使用默认线程工厂,并将创建非守护线程。

相关问题