如何表示Java Future结果的失败

Hun*_*nif 5 java future

据我所知提交Callable/ RunnableExecutorService为去,如果我想在并行执行资源重码的方式.因此我的方法结构:

public class ServiceClass {
    protected final ExecutorService executorService = Executors.newCachedThreadPool();

    public Future<Result> getResult(Object params) {
        if (params == null) {
            return null; // In situations like this the method should fail
        }
        // Do other fast pre-processing stuff
        return executorService.submit(new CallProcessResult(params));
    }

    private class CallProcessResult implements Callable<Result> {
        private Object params;
        public CallProcessResult(Object params) {
            this.params = params;
        }
        @Override
        public Result call() throws Exception {
            // Compute result for given params
            // Failure may happen here too!
            return result;
        }
    }
}
public class Result {
    ...
}
Run Code Online (Sandbox Code Playgroud)

我在上面的代码中标记了2个可能发生故障的位置.对于这两种情况,可用于错误处理的选项是完全不同的.

在提交任务之前,可能会出现无效参数,一些可能失败的快速预处理代码等问题.

我在这里看到了几种表示失败的方法:

  1. 如果提供无效params,则getResult立即返回null.在这种情况下,我getResult每次调用时都要检查是否返回null.
  2. 抛出已检查的异常而不是上述异常.
  3. 实例化一个Future<Result>get()请求时返回null .我会用Apache Commons做到这一点ConcurrentUtils.constantFuture(null).在这种情况下,我希望getResult总是返回一些非null Future<Result>.我更喜欢这个选项,因为它与第二种情况一致.

在任务执行期间,我可能会遇到严重错误,例如内存不足,文件损坏,文件不可用等.

  1. 我想在我的情况下更好的选择是返回null,因为任务的结果是一个对象.
  2. 此外,我可以抛出已检查的异常并处理它们ThreadPoolExecutor.afterExecute(如NiranjanBhat所示).请参阅从Java ExecutorService任务处理异常

哪种情况更好(在两种情况下)?

也许有不同的方法来做这个或我应该使用的设计模式?

Tom*_*son 9

我建议在任务处理期间失败,你只需抛出一个适当的异常.不要在执行程序中为此添加任何特殊处理.会发生什么,它将被捕获,并存储在Future.当Futureget方法被调用,它会抛出一个ExecutionException,其中的调用get就可以解压缩和处理.这基本上是将正常的异常处理转换为Callable/ Future范例的方式.这看起来像这样:

    Future<Result> futureResult = serviceClass.getResult("foo");

    try {
        Result result = futureResult.get();
        // do something with result
    }
    catch (ExecutionException ee) {
        Throwable e = ee.getCause();
        // do something with e
    }
Run Code Online (Sandbox Code Playgroud)

鉴于调用者get必须对ExecutionExceptions进行处理,您可以利用它来处理提交期间的失败.要做到这一点,你可以构造一个Future类似于Apache Commons的constantFuture,但它抛出给定的异常而不是返回给定的值.我不认为JDK中有类似的东西,但编写简单(如果单调乏味):

public class FailedFuture<T> implements Future<T> {
    private final Throwable exception;

    public FailedFuture(Throwable exception) {
        this.exception = exception;
    }

    @Override
    public T get() throws ExecutionException {
        throw new ExecutionException(exception);
    }

    @Override
    public T get(long timeout, TimeUnit unit) throws ExecutionException {
        return get();
    }

    @Override public boolean cancel(boolean mayInterruptIfRunning) { return false; }
    @Override public boolean isCancelled() { return false; }
    @Override public boolean isDone() { return true; }
}
Run Code Online (Sandbox Code Playgroud)

这有些狡猾 - 你在同步调用的方法中失败,并在异步调用的方法中看起来像一个失败.您正在将处理错误的负担从实际导致它的代码转移到稍后运行的某些代码.但是,它确实意味着您可以在一个地方拥有所有故障处理代码; 这可能足以让它变得有价值.