装饰Java的callable以添加行为

时间:2016-02-12 15:15:05

标签: java multithreading concurrency threadpool callable

让我们假设我们有可能长期运行的任务:

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. 从设计模式视图和性能视图中解决此问题的最佳方法是什么?

  4. 谢谢:D

1 个答案:

答案 0 :(得分:1)

围绕RetryableCallable的{​​{1}}换行InterruptibleCallable并在每次LongRunningTask执行时创建两个额外的执行程序是不好的。

RetryableCallable捕获RetryableCallable时,它通常不会再次运行相同的任务。这有点令人困惑,因为如果任务被超时“杀死”,你为什么要再次运行它? 另外为什么你需要在这里创建另一个执行者?保持简单

TimeoutException

它不应该使用超时和延迟,它是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

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

  • 在提交它的主题中调用RetryableCallableWithDelayAndTimeoutAndSomethingElse
  • 限制任务在get(time, timeunit)函数内的执行时间,例如通过“定期”检查,我们还有更多时间。
  • 使用自定义执行器和一个线程Auditor创建自己的执行器类,它将接受一些扩展call()并具有TimeLimitedCallable功能的Callable。审计员将控制实施int getTimeLimit()的所有正在运行的任务的时间范围。
  • 为“任务审核员”单独执行一个程序,并在将一个(或一堆)任务与业务逻辑一起提交到主执行程序之后 - 创建审计任务并将其提交给单独的执行程序。