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 个可能发生故障的地方。对于这两种情况,可用于错误处理的选项完全不同。
提交任务前可能会出现参数无效、一些快速预处理代码可能会失败等问题。
我在这里看到了几种表示失败的方法:
- 如果无效
params
提供给getResult
立即返回 null。在这种情况下,我必须检查是否getResult
每次我调用它时都返回 null。 - 抛出已检查的异常而不是上述异常。
- 实例化一个
Future<Result>
在get()
上返回 null要求。我会用 Apache Commons 做到这一点ConcurrentUtils.constantFuture(null)
.在这种情况下,我希望getResult
总是返回一些非空Future<Result>
.我更喜欢这个选项,因为它符合第二种情况。
在任务执行期间我可能会遇到严重的错误,例如内存不足、文件损坏、文件不可用等。
- 我想在我的情况下更好的选择是返回 null,因为任务的结果是一个对象。
- 此外,我可以抛出已检查的异常并在
ThreadPoolExecutor.afterExecute
中处理它们(正如 NiranjanBhat 所建议的那样)。参见 Handling exceptions from Java ExecutorService tasks
哪种做法更好(在两种情况下)?
也许有不同的方法或我应该使用的设计模式?
最佳答案
我建议对于任务处理期间的失败,您只需抛出一个适当的异常即可。不要在执行器中为此添加任何特殊处理。将会发生的是它会被捕获,并存储在 Future
中。当Future
的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
的调用者必须对 ExecutionException
进行这种处理,因此您可以利用它来处理提交期间的失败。为此,您可以构造一个类似于 Apache Commons 的 constantFuture
的 Future
,但它会抛出给定的异常而不是返回给定的值。我不认为 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; }
}
这有点狡猾 - 您在同步调用方法期间失败,并使其看起来像在异步调用方法期间失败。您正在将处理错误的负担从实际导致它的代码转移到稍后运行的代码。不过,这确实意味着您可以将所有故障处理代码放在一个地方;这可能足以让这一切变得有值(value)。
关于java - 如何表示 Java Future 结果失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13928818/