<线程池-定时任务> ScheduledExecutorService之shutdown引发的RejectedExecutionException问题

时间:2023-03-10 03:25:29
<线程池-定时任务> ScheduledExecutorService之shutdown引发的RejectedExecutionException问题

一、 问题描述 
先来看一下异常信息,启动tomcat时就报错:

  1. 2015-3-20 15:22:39 org.apache.catalina.core.StandardContext listenerStart
  2. 严重: Exception sending context initialized event to listener instance of class
  3. com.***.***.action.GateWayMonitorListener
  4. java.util.concurrent.RejectedExecutionException
  5. at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution
  6. (ThreadPoolExecutor.java:1774)
  7. at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.jav
  8. a:768)
  9. at java.util.concurrent.ScheduledThreadPoolExecutor.delayedExecute(Sched
  10. uledThreadPoolExecutor.java:215)
  11. at java.util.concurrent.ScheduledThreadPoolExecutor.scheduleWithFixedDel
  12. ay(ScheduledThreadPoolExecutor.java:443)
  13. at com.***.***.action.GateWayMonitorListener
  14. .scheduleEmailAndSms(GateWayMonitorListener.java:77)
  15. at com.***.***.action.GateWayMonitorListener
  16. .contextInitialized(GateWayMonitorListener.java:67)
  17. at org.apache.catalina.core.StandardContext.listenerStart(StandardContex
  18. t.java:4206)
  19. at org.apache.catalina.core.StandardContext.start(StandardContext.java:4
  20. 705)
  21. at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase
  22. .java:799)
  23. at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:77
  24. 9)
  25. at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:601)
  26. at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.jav
  27. a:1079)
  28. at org.apache.catalina.startup.HostConfig.deployDirectories(HostConfig.j
  29. ava:1002)
  30. at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:506
  31. )
  32. at org.apache.catalina.startup.HostConfig.start(HostConfig.java:1317)
  33. at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java
  34. :324)
  35. at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(Lifecycl
  36. eSupport.java:142)
  37. at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1065)
  38. at org.apache.catalina.core.StandardHost.start(StandardHost.java:840)
  39. at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1057)
  40. at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:463
  41. )
  42. at org.apache.catalina.core.StandardService.start(StandardService.java:5
  43. 25)
  44. at org.apache.catalina.core.StandardServer.start(StandardServer.java:754
  45. )
  46. at org.apache.catalina.startup.Catalina.start(Catalina.java:595)
  47. at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  48. at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.
  49. java:39)
  50. at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAcces
  51. sorImpl.java:25)
  52. at java.lang.reflect.Method.invoke(Method.java:597)
  53. at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:289)
  54. at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:414)
  55. 2015-3-20 15:22:39 org.apache.catalina.core.StandardContext start
  56. 严重: Error listenerStart

再来看看相关源码:

  1. public class GateWayMonitorListener implements ServletContextListener, ApplicationContextAware
  2. {
  3. /**
  4. *  设定两个线程,一个用来定期发送巡检短信;另一个定期执行三大运营商网关监控。
  5. */
  6. public static ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
  7. public void contextInitialized(ServletContextEvent event)
  8. {
  9. // 网关监测
  10. GateWayMonitorListener.service.scheduleWithFixedDelay(new MonitorTask(), 60, MonitorConfigCache.lastPollingIntervalSecond, TimeUnit.SECONDS);
  11. // 定时每天固定时间发送短信
  12. GateWayMonitorListener.service.scheduleAtFixedRate(new EveryDaySmsTask(), calculateIntialDelay(), GateWayMonitorListener.ONE_DAY_IN_MILLISECONDS, TimeUnit.MILLISECONDS);
  13. }
  14. }
  15. /**
  16. * 按新时间间隔重新设置定时任务。
  17. *
  18. * @param interval
  19. */
  20. public static synchronized void resetMonitorTaskInterval()
  21. {
  22. // 关闭老的线程池
  23. GateWayMonitorListener.service.shutdownNow();
  24. try
  25. {
  26. GateWayMonitorListener.service.awaitTermination(60, TimeUnit.SECONDS);
  27. }
  28. catch (InterruptedException e)
  29. {
  30. log.warn("InterruptedException when shutdown old threadPool", e);
  31. }
  32. catch (Exception e)
  33. {
  34. log.error("other exception when shutdown old threadPool", e);
  35. }
  36. GateWayMonitorListener.service = Executors.newScheduledThreadPool(2);
  37. // 重新设置定时任务。
  38. }

没有列出的还有另一个类会定时重新加载定时任务的配置文件,如果配置文件修改了定时任务执行的时间设置,则重新配置定时任务,起到热加载配置文件的目的。也就是会调用resetMonitorTaskInterval()方法。

二、 问题分析 
Tomcat一启动就报错RejectedExecutionException, 
(1)根据jdk描述何时回抛出这个异常:if the task cannot be scheduled for execution,只是说任务在不能被加入线程池时会抛出这个异常,不够明确。 
(2)进一步分析什么时候线程池不能加入线程来执行,如果不是线程池还未被初始化好,线程池已满且策略是超过的会被拒绝外,就是线程池被shutdown了。Tomcat一启动就报错,而且线程池是在最上面static的,那应该就是被shutdown了。查看是否有哪里调用shutdown方法了。 
(3)再分析,果然在另一个线程处会调用重新设置线程池的方法resetMonitorTaskInterval(),而且里面调用了shutdown。问题找到了,第一次读配置文件时就触发了重新设置定时任务。这是不行滴。。第一次只算是初始化,后面发现变更才应该重设线程池。但一定要控制同步,保证shutdown之后没有再往旧线程池中加入定时任务。

三、 问题解决 
控制线程池的shutdown调用,调用后不能再往线程池里加入线程。

原文地址:http://zoroeye.iteye.com/blog/2194390