AFAIK submitting Callable
/Runnable
to ExecutorService
is the way to go if I want to execute resource-heavy code in parallel. Hence my method structure:
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 {
...
}
I have marked 2 spots in the code above in which failures can happen. The options available for error handling are quite different for those 2 cases.
Before submitting the task there can be issues like invalid parameters, some fast pre-processing code that may fail.
I see several ways to signify failure here:
params
supplied to getResult
return null immediately. In this case I'll have to check if getResult
returned null every time I call it.Future<Result>
that returns null on get()
request. I would do that with Apache Commons ConcurrentUtils.constantFuture(null)
. In this case I would expect getResult
to always return some non-null Future<Result>
. I like this option more, because it is consistent with the second case.During task execution I can expect serious errors like lack of memory, corrupted files, unavailable files etc.
ThreadPoolExecutor.afterExecute
(as suggested by NiranjanBhat). See Handling exceptions from Java ExecutorService tasks
Which is the better practice (in both cases)?
Perhaps there is a different way to do this or a design pattern I should use?
A Future represents the result of an asynchronous computation. Methods are provided to check if the computation is complete, to wait for its completion, and to retrieve the result of the computation.
When one submit a task to ExecutorService which is take a long running time, then it returns a Future object immediately. This Future object can be used for task completion and getting result of computation.
Future , represents the result of an asynchronous computation. When the asynchronous task is created, a Java Future object is returned. This Future object functions as a handle to the result of the asynchronous task.
Simply put, the Future class represents a future result of an asynchronous computation. This result will eventually appear in the Future after the processing is complete. Let's see how to write methods that create and return a Future instance.
I would suggest that for failure during task processing, you simply throw an appropriate exception. Don't add any special handling for this in the executor. What will happen is that it will be captured, and stored in the Future
. When the Future
's get
method is called, it will throw an ExecutionException
, which the caller of get
can then unpack and handle. This is essentially how normal exception handling is transposed into the Callable
/Future
paradigm. This looks like this:
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
}
Given that the caller of get
has to have this handling of ExecutionException
s, you could then take advantage of that to deal with failure during submission. To do this, you could construct a Future
that is like Apache Commons's constantFuture
, but which throws a given exception rather than returns a given value. I don't think there's anything like that in the JDK, but it's simple (if tedious) to write:
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; }
}
This is somewhat dodgy - you're taking a failure during a synchronously-called method, and making it look like a failure during the asynchronously-called method. You're shifting the burden of handling the error from the code that actually caused it to some code that runs later. Still, it does mean you can have all the failure handling code in one place; that might be enough of an advantage to make this worthwhile.
You can use afterExecute method. This is defined in the ThreadPoolExecutor, which you will need to override.
This method is called after the execution of each task is completed. You will get the task instance in this callback method. You can record the errors in some variable in your task and access it in this method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With