如何表示Java Future结果的失败

时间:2012-12-18 08:14:04

标签: java future

如果我想并行执行资源密集型代码,那么AFAIK向Callable提交Runnable / ExecutorService是可行的方法。因此我的方法结构:

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 {
    ...
}

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

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

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

  1. 如果提供给params的{​​{1}}无效,则立即返回null。在这种情况下,我每次调用时都要检查getResult是否返回null。
  2. 抛出已检查的异常,而不是上述内容。
  3. 实例化getResult,在Future<Result>请求中返回null。我会用Apache Commons get()做到这一点。在这种情况下,我希望ConcurrentUtils.constantFuture(null)始终返回一些非空getResult。我更喜欢这个选项,因为它与第二种情况一致。
  4. 在执行任务期间我可能会遇到严重错误,例如内存不足,文件损坏,文件不可用等等。

    1. 我认为在我的情况下的更好选项是返回null,因为任务的结果是一个对象。
    2. 此外,我可以抛出已检查的异常并在Future<Result>处理它们(如NiranjanBhat所建议的那样)。请参阅Handling exceptions from Java ExecutorService tasks
    3. 哪种更好的做法(在两种情况下都是这样)?

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

2 个答案:

答案 0 :(得分:9)

我建议在任务处理过程中失败,你只需抛出一个适当的异常。不要在执行程序中为此添加任何特殊处理。将会发生的是它将被捕获并存储在Future中。当Future的{​​{1}}方法被调用时,它会抛出get,然后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 } 的调用者必须对get进行此处理,您可以利用它来处理提交期间的失败。为此,您可以构造一个类似于Apache Commons ExecutionException的{​​{1}},但它会抛出给定的异常而不是返回给定的值。我不认为JDK中有类似的东西,但写起来很简单(如果单调乏味):

Future

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

答案 1 :(得分:2)

您可以使用afterExecute方法。这是在ThreadPoolExecutor中定义的,您需要覆盖它。
在完成每个任务的执行后调用此方法。您将在此回调方法中获取任务实例。您可以在任务中的某个变量中记录错误,并使用此方法访问它。