装饰 Java 的可调用以添加行为

Decorating Java's callable to add behavior

假设我们有潜在的长运行宁任务:

public class LongRunningTask {
    public ReturnType doSomething() {
        ...
    }
}

并且我们希望 运行 同时执行许多这些任务。

所以,我有我的要求:

public class LongRunningCallable implements Callable<LongRunningTask> {
    private final LongRunningTask task;
    ...
    public ReturnType call() {
        return task.doSomething();
    }
    ...
}

现在,由于这可能真的会持续很长时间,我可能想将其限制为 运行 仅用于特定数量。所以,我可能会这样做:

public class InterruptibleCallable<T> implements Callable<T> {
        protected final long timeout;
        protected final TimeUnit timeUnit;
        protected final Callable<T> callable;

        public InterruptibleCallable(long timeout, TimeUnit timeUnit, Callable<T> callable) {
                this.timeout = timeout;
                this.timeUnit = timeUnit;
                this.callable = callable;
        }

        @Override
        public T call() throws Exception {

                ExecutorService executorService = Executors.newSingleThreadExecutor();

                T result = null;

                try {
                    result = executorService.submit(callable).get(timeout, timeUnit);
                } catch (InterruptedException e) {
                    LOGGER.error("Callable: " + callable.toString() + " was interrupted");
                    throw e;
                } catch (ExecutionException e) {
                    throw e;
                } catch (TimeoutException e) {
                    LOGGER.warn("Callable: " + callable.toString() + " timed out after " + timeout + " " + timeUnit);
                    throw e;
                } finally {
                    executorService.shutdown();
                }

                return result;
        }
}

没关系,但现在我还想包装它,以便它可以在遇到异常时重试(延迟):

public class RetryableCallable<T> implements Callable<T> {
    private final long delay;
    private final TimeUnit timeUnit;
    private final Callable<T> callable;

    @Override
    public T call() throws Exception {
        T result = null;

        try {
            ExecutorService executorService = Executors.newSingleThreadExecutor();
            result = executorService.submit(this.callable).get();
        } catch (Exception e) {         
            ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
            result = executorService.schedule(this.callable, delay, timeUnit).get();
        }

        return result;
    }
}

现在,我的问题:

  1. 是否已经有提供此功能(或超集)的库?

  2. 这是一个好的设计吗,尤其是创建另一个执行器并在每个包装器中提交一个可调用项,为什么?

  3. 从设计模式和性能的角度来看,解决这个问题的最佳方法是什么?

谢谢 :D

RetryableCallable 包裹在 InterruptibleCallable 周围,包裹 LongRunningTask 并在每次执行时创建两个额外的执行程序 RetryableCallable 执行是错误的。

RetryableCallable 捕获 TimeoutException 通常它不应该再次 运行 相同的任务。这有点令人困惑,因为如果任务超时 "killed" 为什么要再 运行 一次? 另外为什么需要在这里创建另一个执行器?保持简单

public class RetryableCallable<T> implements Callable<T> {
    private int retries = 3;

    @Override
    public T call() throws Exception {
        while (retries-- > 0) {
            try {
                return callable.call();
            } catch (InterruptedException e) {
                LOGGER.error("Callable: " + callable.toString() + " was interrupted");
                throw e;
            } catch (TimeoutException e) {
                LOGGER.warn("Callable: " + callable.toString() + " timed out after " + timeout + " " + timeUnit);
                throw e;
            } catch (Exception e) {
                LOGGER.warn("Callable: " + callable.toString() + " failed");
            }
        }
        throw new IllegalStateException();//or return null
    }
}

它不应该使用超时和延迟,它是 RetryableCallable 而不是 RetryableCallableWithDelayAndTimeoutAndSomethingElse

如果你想限制任务的执行时间,我看到至少有 4 个好的方法:

  • 在提交它的线程中调用 get(time, timeunit)
  • 限制任务在 call() 函数内的执行时间,例如通过检查 "periodically" 我们还有更多时间吗。
  • 制作你自己的执行器class,其中包含自定义执行器和一个线程审计器,它会接受一些 TimeLimitedCallable 扩展 Callable 并具有 int getTimeLimit() 功能。审计员将控制实施 TimeLimitedCallable.
  • 的所有 运行ning 任务的时间范围
  • 为 "task's auditors" 创建一个单独的执行器,并在将一个(或一堆)具有业务逻辑的任务提交到主执行器后 - 创建审计任务并将其提交到单独的执行器中。