为什么BoundedExecutor必须捕获RejectedExecutionException?

时间:2017-07-24 19:18:32

标签: java concurrency executor

对于Java Concurrency in Practice一书中的BoundedExecutor,任务提交已被信号量限制。底层执行程序何时抛出RejectedExecutionException?也许当操作系统用完线程时?

public class BoundedExecutor {
  private final Executor exec;
  private final Semaphore semaphore;

  public BoundedExecutor(Executor exec, int bound) {
    this.exec = exec;
    this.semaphore = new Semaphore(bound);
  }

  public void submitTask(final Runnable command) throws InterruptedException, RejectedExecutionException 
  {
    semaphore.acquire();

    try {
        exec.execute(new Runnable() {
            @Override public void run() {
                try {
                    command.run();
                } finally {
                    semaphore.release();
                }
            }
        });
    } catch (RejectedExecutionException e) {
        semaphore.release();
        throw e;
    }
  }
}

2 个答案:

答案 0 :(得分:0)

Executor.execute()的合同的一部分是它可以抛出RejectedExecutionException

  

如果无法接受执行此任务

这对于任何给定的Executor实施意味着什么取决于该实施的自由裁量权。我可以创建一个拒绝任务的OnlyOnSundaysExecutor,除非当周的当天是星期天。您必须查看各种Executor实现的文档,以了解它们会在什么情况下抛出RejectedExecutionException异常。

答案 1 :(得分:0)

无论出现异常的情况如何,即使发生异常,保持应用程序处于一致状态也很重要。

这里,已经获得的信号量应该总是被释放。对于应该在所有情况下都要发布的大多数资源,即使在特殊情况下,也可以使用try {} finally { /* release action */ }构造来确保发布,但是在这里,我们有一个特殊情况,即应该只执行发布操作 在特殊情况下,与成功案例一样,提交的Runnable将执行发布操作(请注意,在Runnable内,确实正在使用finally。)< / p>

因此代码应该在抛出RejectedExecutionException时处理。我们可能希望为每个RuntimeExceptionError执行此操作,但问题是,RejectedExecutionException是唯一的异常类型,我们确信runnable永远不会被执行。对于所有其他类型的例外,它可能仍会运行。

为了使清理安全,您需要另一个原子切换:

public void submitTask(final Runnable command) throws InterruptedException {
    AtomicBoolean proceed = new AtomicBoolean(true);
    semaphore.acquire();
    try {
        exec.execute(new Runnable() {
            @Override public void run() {
                if(proceed.compareAndSet(true, false)) try {
                    command.run();
                } finally {
                    semaphore.release();
                }
            }
        });
    } catch(Throwable e) {
        if(proceed.compareAndSet(true, false)) semaphore.release();
        throw e;
    }
}

现在,在每种特殊情况下,都会尝试释放信号量,除非Runnable标记它已经在运行。或者如果Runnable检测到由于提交代码中的异常而释放了信号量,则try将不会继续。

这当然比书中的例子更复杂,并且可能分散了示例的原始意图。此外,它使用Java 7功能,能够轻松捕获并重新抛出.mask块的所有可能异常。这本书写完后就没用了。