Java:SingleThreadScheduledExecutor& java.util.concurrent.RejectedExecutionException

时间:2009-12-21 02:58:07

标签: java multithreading exception concurrency executor

我有这个问题,我有

private ScheduledExecutorService executor =  
     Executors.newSingleThreadScheduledExecutor(); 

和每50毫秒创建的任务:

executor.scheduleAtFixedRate(myTask, 0, 50, TimeUnit.MILLISECONDS);

myTask有时需要一段时间才能完成(比如2-3秒左右),但是newSingleThreadScheduledExecutor会保证下一个预定的myTask会等到当前的myTask完成。

但是,我不时会收到此错误:

执行:java.util.concurrent.RejectedExecutionException

我该怎么办?感谢

3 个答案:

答案 0 :(得分:13)

考虑执行者正在做什么。根据您的说明,它每50毫秒运行一个任务。假设这个任务运行时间不到50毫秒,那么一切都很好。但是,每隔一段时间运行需要2-3秒。发生这种情况时,执行程序仍然会尝试每50毫秒执行一次,但因为它只有一个线程,所以它不能,并且拒绝那些在长时间运行的任务仍在进行时被触发的执行。这会导致你看到的异常。

你有两个选择来解决这个问题(假设你想坚持使用一个线程):

  1. 使用scheduleWithFixedDelay而不是scheduleAtFixedRate。如果你仔细阅读javadoc,你会发现scheduleWithFixedDelay将在完成一个任务和下一个任务的开始之间等待50毫秒,所以它永远不会“重叠”,即使其中一个需要很长时间时间。相反,scheduleAtFixedRate将尝试每50毫秒执行一次,无论每个花费多长时间。

  2. 更改执行程序处理执行失败的方式。默认情况下是记录异常,但您可以告诉它忽略它,例如。看一下java.util.concurrent.RejectedExecutionHandler的子类,例如DiscardPolicy,它只是默默地删除了无法运行的任务。您可以通过直接构造ScheduledThreadPoolExecutor并将处理程序传递给构造函数来使用它们,而不是使用Executors工厂类。

  3. 我怀疑选项(1)就是你想要的。

答案 1 :(得分:4)

以下任何一种情况都会抛出此异常:

  1. 您已关闭执行程序
  2. 已超出Executor对其工作队列或最大线程的限制。
  3. 我认为后者正在发生。执行任务时需要很长时间,因此无法运行后续计划任务,因为池中没有足够的可用线程。

    或者:

    1. 使用较大的池大小或使用cachedThreadPool
    2. 将拒绝政策更改为例如使用ThreadPoolExecutor.CallerRunsPolicy
    3. 创建一个单独的Executor来运行长期运行任务,并从计划任务中运行这些任务。实际上,您可以使用相同的Executor实例执行此操作,前提是您可以增加池大小。
    4. 另见ThreadPoolExecutor javadoc

答案 2 :(得分:-1)