I have a method that performs some task with a timeout. I use the ExecutorServer.submit() to get a Future object, and then I call future.get() with a timeout. This is working fine, but my question is the best way to handle checked exceptions that can be thrown by my task. The following code works, and preserves the checked exceptions, but it seems extremely clumsy and prone to break if the list of checked exceptions in the method signature changes.
我有一个方法,通过超时执行一些任务。我使用ExecutorServer.submit()来获取Future对象,然后使用超时调用future.get()。这工作正常,但我的问题是处理我的任务可以抛出的已检查异常的最佳方法。以下代码可以工作,并保留已检查的异常,但如果方法签名中的已检查异常列表发生更改,则它似乎非常笨拙且容易中断。
Any suggestions on how to fix this? I need to target Java 5, but I'd also be curious to know if there are good solutions in newer versions of Java.
对于如何解决这个问题,有任何的建议吗?我需要针对Java 5,但我也很想知道在较新版本的Java中是否有好的解决方案。
public static byte[] doSomethingWithTimeout( int timeout ) throws ProcessExecutionException, InterruptedException, IOException, TimeoutException {
Callable<byte[]> callable = new Callable<byte[]>() {
public byte[] call() throws IOException, InterruptedException, ProcessExecutionException {
//Do some work that could throw one of these exceptions
return null;
}
};
try {
ExecutorService service = Executors.newSingleThreadExecutor();
try {
Future<byte[]> future = service.submit( callable );
return future.get( timeout, TimeUnit.MILLISECONDS );
} finally {
service.shutdown();
}
} catch( Throwable t ) { //Exception handling of nested exceptions is painfully clumsy in Java
if( t instanceof ExecutionException ) {
t = t.getCause();
}
if( t instanceof ProcessExecutionException ) {
throw (ProcessExecutionException)t;
} else if( t instanceof InterruptedException ) {
throw (InterruptedException)t;
} else if( t instanceof IOException ) {
throw (IOException)t;
} else if( t instanceof TimeoutException ) {
throw (TimeoutException)t;
} else if( t instanceof Error ) {
throw (Error)t;
} else if( t instanceof RuntimeException) {
throw (RuntimeException)t;
} else {
throw new RuntimeException( t );
}
}
}
=== UPDATE ===
===更新===
Many people posted responses that recommended either 1) re-throwing as a general exception, or 2) re-throw as an unchecked exception. I don't want to do either of these, because these exception types (ProcessExecutionException, InterruptedException, IOException, TimeoutException) are important - they will each be handled differently by the calling processed. If I were not needing a timeout feature, then I would want my method to throw these 4 specific exception types (well, except for TimeoutException). I don't think that adding a timeout feature should change my method signature to throw a generic Exception type.
许多人发布的回复建议:1)重新投掷作为一般例外,或2)作为未经检查的例外重新投掷。我不想做其中任何一个,因为这些异常类型(ProcessExecutionException,InterruptedException,IOException,TimeoutException)很重要 - 它们将由处理的调用以不同方式处理。如果我不需要超时功能,那么我希望我的方法抛出这4种特定的异常类型(好吧,除了TimeoutException)。我不认为添加超时功能应该改变我的方法签名以抛出一般的异常类型。
10 个解决方案
#1
15
I've looked at this problem in depth, and it's a mess. There is no easy answer in Java 5, nor in 6 or 7. In addition to the clumsiness, verbosity and fragility that you point out, your solution actually has the problem that the ExecutionException
that you are stripping off when you call getCause()
actually contains most of the important stack trace information!
我已经深入研究了这个问题,这是一团糟。在Java 5中没有简单的答案,在6或7中也没有简单的答案。除了你指出的笨拙,冗长和脆弱之外,你的解决方案实际上还有一个问题,就是当你调用getCause()时你正在剥离的ExecutionException包含大部分重要的堆栈跟踪信息!
That is, all the stack information of the thread executing the method in the code you presented is only in the ExcecutionException, and not in the nested causes, which only cover frames starting at call()
in the Callable. That is, your doSomethingWithTimeout
method won't even appear in the stack traces of the exceptions you are throwing here! You'll only get the disembodied stack from the executor. This is because the ExecutionException
is the only one that was created on the calling thread (see FutureTask.get()
).
也就是说,在您呈现的代码中执行方法的线程的所有堆栈信息仅在ExcecutionException中,而不在嵌套原因中,这些原因仅覆盖Callable中call()处开始的帧。也就是说,你的doSomethingWithTimeout方法甚至不会出现在你抛出的异常的堆栈跟踪中!你只能从执行者那里得到无实体的堆栈。这是因为ExecutionException是在调用线程上创建的唯一一个(请参阅FutureTask.get())。
The only solution I know is complicated. A lot of the problem originates with the liberal exception specification of Callable
- throws Exception
. You can define new variants of Callable
which specify exactly which exceptions they throw, such as:
我所知道的唯一解决方案很复杂。很多问题都源于Callable的*异常规范 - 抛出异常。您可以定义Callable的新变体,它们准确指定它们抛出的异常,例如:
public interface Callable1<T,X extends Exception> extends Callable<T> {
@Override
T call() throws X;
}
This allows methods which executes callables to have a more precise throws
clause. If you want to support signatures with up to N exceptions, you'll need N variants of this interface, unfortunately.
这允许执行callables的方法具有更精确的throws子句。如果您想支持最多N个例外的签名,遗憾的是,您将需要此接口的N个变体。
Now you can write a wrapper around the JDK Executor
which takes the enhanced Callable, and returns an enhanced Future
, something like guava's CheckedFuture. The checked exception type(s) are propagated at compile time from the creation and type of the ExecutorService
, to the returned Future
s, and end up on the getChecked
method on the future.
现在你可以在JDK Executor周围编写一个包装器,它接受增强的Callable,并返回一个增强的Future,比如guava的CheckedFuture。已检查的异常类型在编译时从ExecutorService的创建和类型传播到返回的Futures,并最终在未来的getChecked方法上传播。
That's how you thread the compile-time type safety through. This means that rather than calling:
这就是你通过编译时类型安全的方式。这意味着而不是调用:
Future.get() throws InterruptedException, ExecutionException;
You can call:
你可以打电话:
CheckedFuture.getChecked() throws InterruptedException, ProcessExecutionException, IOException
So the unwrapping problem is avoided - your method immediately throws the exceptions of the required type and they are available and checked at compile time.
因此,避免了解包问题 - 您的方法立即抛出所需类型的异常,并且它们在编译时可用并检查。
Inside getChecked
, however you still need to solve the "missing cause" unwrapping problem described above. You can do this by stitching the current stack (of the calling thread) onto the stack of the thrown exception. This a stretching the usual use of a stack trace in Java, since a single stack stretches across threads, but it works and is easy to understand once you know what is going on.
在getChecked内部,你仍然需要解决上面描述的“缺失原因”展开问题。您可以通过将当前堆栈(调用线程)拼接到抛出异常的堆栈上来完成此操作。这延伸了Java中堆栈跟踪的常规用法,因为单个堆栈跨越线程,但是一旦您知道发生了什么,它就可以工作并且易于理解。
Another option is to create another exception of the same thing as the one being thrown, and set the original as the cause of the new one. You'll get the full stack trace, and the cause relationship will be the same as how it works with ExecutionException
- but you'll have the right type of exception. You'll need to use reflection, however, and is not guaranteed to work, e.g., for objects with no constructor having the usual parameters.
另一种选择是创建与被抛出的异常相同的异常,并将原始异常设置为新原因。您将获得完整的堆栈跟踪,并且原因关系与它与ExecutionException的工作方式相同 - 但您将拥有正确的异常类型。但是,您需要使用反射,并且不能保证工作,例如,对于没有具有常用参数的构造函数的对象。
#2
1
Here are couple of interesting information for checked and against Checked Exceptions. Brian Goetz discussion and an argument of against checked exceptions from Eckel Discussion. But I did not know if you have already implemented and given a thought about the checked exception refactor that is discussed by Joshua in this book.
以下是检查和反对Checked Exceptions的几个有趣信息。 Brian Goetz的讨论和反对Eckel Discussion中检查异常的论点。但我不知道你是否已经实现并考虑了约书亚在本书中讨论的经过检查的异常重构。
According the Effective Java pearls, one of the preferred method of handling Checked exceptions is to turn a checked exception into an Un-Checked Exception. So for example,
根据Effective Java珍珠,处理Checked异常的首选方法之一是将已检查的异常转换为Un-Checked Exception。例如,
try{
obj.someAction()
}catch(CheckedException excep){
}
change this implementation to
将此实现更改为
if(obj.canThisOperationBeperformed){
obj.someAction()
}else{
// Handle the required Exception.
}
#3
1
I'm afraid there's no answer to your problem. Basically, you are launching a task in a different thread than the one you are in, and want to use the ExecutorService pattern to catch all the exceptions that task can throw, plus the bonus of interrupting that task after a certain amount of time. Your approach is the right one : you couldnt do that with a bare Runnable.
我担心你的问题没有答案。基本上,您在与您所在的线程不同的线程中启动任务,并希望使用ExecutorService模式捕获任务可以抛出的所有异常,以及在一定时间后中断该任务的奖励。你的方法是正确的:你无法用裸露的Runnable做到这一点。
And this exception, that you have no information about, you want to throw it again, with a certain type : ProcessExecutionException, InterruptedException or IOException. If it's another type, you want to rethrow it as a RuntimeException (which is btw not the best solution, since you dont cover all the cases).
而这个例外,你没有任何信息,你想再次抛出它,具有某种类型:ProcessExecutionException,InterruptedException或IOException。如果它是另一种类型,你想重新抛出它作为RuntimeException(这不是最好的解决方案,因为你没有涵盖所有的情况)。
So you have an impendance mismatch there : a Throwable on one hand, and a known exception type on the other. The only solution you have to solve it is to do what you've done : check the type, and throw it again with a cast. It can be written differently, but will look the same in the end...
所以你在那里有一个阻碍不匹配:一方面是Throwable,另一方面是一个已知的异常类型。你必须解决它的唯一解决方案是做你已经完成的事情:检查类型,然后再使用强制转换再次抛出它。它可以用不同的方式编写,但最终看起来会一样......
#4
1
Here's what I do in this situation. This accomplishes the following:
这是我在这种情况下所做的。这实现了以下目的:
- Re-throws checked exceptions without wrapping them
- Glues together the stack traces
重新抛出已检查的异常而不包装它们
将堆栈轨迹粘在一起
Code:
public <V> V waitForThingToComplete(Future<V> future) {
boolean interrupted = false;
try {
while (true) {
try {
return future.get();
} catch (InterruptedException e) {
interrupted = true;
}
}
} catch (ExecutionException e) {
final Throwable cause = e.getCause();
this.prependCurrentStackTrace(cause);
throw this.<RuntimeException>maskException(cause);
} catch (CancellationException e) {
throw new RuntimeException("operation was canceled", e);
} finally {
if (interrupted)
Thread.currentThread().interrupt();
}
}
// Prepend stack frames from the current thread onto exception trace
private void prependCurrentStackTrace(Throwable t) {
final StackTraceElement[] innerFrames = t.getStackTrace();
final StackTraceElement[] outerFrames = new Throwable().getStackTrace();
final StackTraceElement[] frames = new StackTraceElement[innerFrames.length + outerFrames.length];
System.arraycopy(innerFrames, 0, frames, 0, innerFrames.length);
frames[innerFrames.length] = new StackTraceElement(this.getClass().getName(),
"<placeholder>", "Changed Threads", -1);
for (int i = 1; i < outerFrames.length; i++)
frames[innerFrames.length + i] = outerFrames[i];
t.setStackTrace(frames);
}
// Checked exception masker
@SuppressWarnings("unchecked")
private <T extends Throwable> T maskException(Throwable t) throws T {
throw (T)t;
}
Seems to work.
似乎工作。
#5
0
I'm not sure why you have the if/else block in the catch and instanceof
, I think you can do what you want with:-
我不确定你为什么在catch和instanceof中有if / else块,我认为你可以做你想要的: -
catch( ProcessExecutionException ex )
{
// handle ProcessExecutionException
}
catch( InterruptException ex )
{
// handler InterruptException*
}
One thing to consider, to reduce clutter, is to catch the exception inside your callable method and re-throw as your own domain/package specific exception or exceptions. How many exceptions you need to create would largely depend on how your calling code will respond to the exception.
要减少混乱,要考虑的一件事是捕获可调用方法中的异常并将其作为自己的域/包特定异常重新抛出。您需要创建多少例外很大程度上取决于您的调用代码将如何响应异常。
#6
0
The javadoc of java.util.concurrent.Future.get()
states the following. Then why not just catch ExecutionException (and Cancellation and Interrupted as declared by the java.util.concurrent.Future.get()
) method?
java.util.concurrent.Future.get()的javadoc声明如下。那么为什么不捕获ExecutionException(以及java.util.concurrent.Future.get()声明的方法取消和中断)?
...
Throws:CancellationException - if the computation was cancelled
CancellationException - 如果计算被取消
ExecutionException - if the computation threw an exception
ExecutionException - 如果计算引发异常
InterruptedException - if the current thread was interrupted while waiting
InterruptedException - 如果当前线程在等待时被中断
So basically you can throw whatever exception within your callable and just catch ExecutionException
. Then ExecutionException.getCause()
will hold the actual exception your callable threw as stated in the javadoc. This way you are shielded from method signature changes related to checked exception declaration.
所以基本上你可以在你的callable中抛出任何异常,然后捕获ExecutionException。然后ExecutionException.getCause()将保存您的callable抛出的实际异常,如javadoc中所述。这样,您就可以屏蔽与已检查异常声明相关的方法签名更改。
By the way you should never catch Throwable
, as this would catch also RuntimeExceptions
and Errors
. Catching Exception
is a little bit better but still not recommended, as it will catch RuntimeExceptions
.
顺便说一下,你永远不应该捕获Throwable,因为这也会捕获RuntimeExceptions和Errors。捕获异常有点好,但仍然不推荐,因为它会捕获RuntimeExceptions。
Something like:
try {
MyResult result = myFutureTask.get();
} catch (ExecutionException e) {
if (errorHandler != null) {
errorHandler.handleExecutionException(e);
}
logger.error(e);
} catch (CancellationException e) {
if (errorHandler != null) {
errorHandler.handleCancelationException(e);
}
logger.error(e);
} catch (InterruptedException e) {
if (errorHandler != null) {
errorHandler.handleInterruptedException(e);
}
logger.error(e);
}
#7
0
In the calling class, catch the Throwable
last. For instance,
在调用类中,最后捕获Throwable。例如,
try{
doSomethingWithTimeout(i);
}
catch(InterruptedException e){
// do something
}
catch(IOException e){
// do something
}
catch(TimeoutException e){
// do something
}
catch(ExecutionException e){
// do something
}
catch(Throwable t){
// do something
}
And the content of doSomethingWithTimeout(int timeout)
should look like this,
doSomethingWithTimeout(int timeout)的内容应该如下所示,
.
.
.
ExecutorService service = Executors.newSingleThreadExecutor();
try {
Future<byte[]> future = service.submit( callable );
return future.get( timeout, TimeUnit.MILLISECONDS );
}
catch(Throwable t){
throw t;
}
finally{
service.shutdown();
}
And it's method signature should look like,
它的方法签名应该是这样的,
doSomethingWithTimeout(int timeout) throws Throwable
doSomethingWithTimeout(int timeout)抛出Throwable
#8
-1
Here is another way to do it, though I'm not convinced that this is less clumsy or less prone to break than to do it with an instanceof check as in your question:
这是另一种方法,虽然我不相信这不像你在问题中那样用笨拙或不太容易破解而不是像检查实例一样:
public static byte[] doSomethingWithTimeout(int timeout)
throws ProcessExecutionException, InterruptedException, IOException, TimeoutException {
....
try {
....
return future.get(1000, TimeUnit.MILLISECONDS);
.....
} catch (ExecutionException e) {
try {
throw e.getCause();
} catch (IOException ioe) {
throw ioe;
} catch (InterruptedException ie) {
throw ie;
} catch (ProcessExecutionException pee) {
throw pee;
} catch (Throwable t) {
//Unhandled exception from Callable endups here
}
} catch (TimeoutException e) {
throw e;
} catch.....
}
#9
-1
I wouldn't say I recommend this, but here is a way you can do it. It is type-safe and whoever comes to modify it after you will probably be unhappy with it.
我不会说我推荐这个,但这是你可以做到的一种方式。它是类型安全的,无论谁来修改它都可能会对它不满意。
public class ConsumerClass {
public static byte[] doSomethingWithTimeout(int timeout)
throws ProcessExecutionException, InterruptedException, IOException, TimeoutException {
MyCallable callable = new MyCallable();
ExecutorService service = Executors.newSingleThreadExecutor();
try {
Future<byte[]> future = service.submit(callable);
return future.get(timeout, TimeUnit.MILLISECONDS);
} catch (ExecutionException e) {
throw callable.rethrow(e);
} finally {
service.shutdown();
}
}
}
// Need to subclass this new callable type to provide the Exception classes.
// This is where users of your API have to pay the price for type-safety.
public class MyCallable extends CallableWithExceptions<byte[], ProcessExecutionException, IOException> {
public MyCallable() {
super(ProcessExecutionException.class, IOException.class);
}
@Override
public byte[] call() throws ProcessExecutionException, IOException {
//Do some work that could throw one of these exceptions
return null;
}
}
// This is the generic implementation. You will need to do some more work
// if you want it to support a number of exception types other than two.
public abstract class CallableWithExceptions<V, E1 extends Exception, E2 extends Exception>
implements Callable<V> {
private Class<E1> e1;
private Class<E2> e2;
public CallableWithExceptions(Class<E1> e1, Class<E2> e2) {
this.e1 = e1;
this.e2 = e2;
}
public abstract V call() throws E1, E2;
// This method always throws, but calling code can throw the result
// from this method to avoid compiler errors.
public RuntimeException rethrow(ExecutionException ee) throws E1, E2 {
Throwable t = ee.getCause();
if (e1.isInstance(t)) {
throw e1.cast(t);
} else if (e2.isInstance(t)) {
throw e2.cast(t);
} else if (t instanceof Error ) {
throw (Error) t;
} else if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new RuntimeException(t);
}
}
}
#10
-2
I've found one way to solve the issue. If it's ExecutionException you can get original one by calling exception.getCause() Then you need to wrap your exception in some kind of Runtime Exception or (what is the best way for me) use @SneakyThrows annotation from project lombok (https://projectlombok.org/). I give a small piece of code example. In addition you can add a few instanceof checks before throwing an exception to be sure this is the one you're expecting.
我找到了解决问题的方法之一。如果它是ExecutionException,你可以通过调用exception.getCause()得到原始的那个然后你需要在某种运行时异常中包装你的异常或(对我来说最好的方法)使用项目lombok中的@SneakyThrows注释(https:// projectlombok.org/)。我给出了一小段代码示例。此外,您可以在抛出异常之前添加一些instanceof检查,以确保这是您期望的那个。
@SneakyThrows
public <T> T submitAndGet(Callable<T> task) {
try {
return executor.submit(task).get(5, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
throw e.getCause();
}
}
#1
15
I've looked at this problem in depth, and it's a mess. There is no easy answer in Java 5, nor in 6 or 7. In addition to the clumsiness, verbosity and fragility that you point out, your solution actually has the problem that the ExecutionException
that you are stripping off when you call getCause()
actually contains most of the important stack trace information!
我已经深入研究了这个问题,这是一团糟。在Java 5中没有简单的答案,在6或7中也没有简单的答案。除了你指出的笨拙,冗长和脆弱之外,你的解决方案实际上还有一个问题,就是当你调用getCause()时你正在剥离的ExecutionException包含大部分重要的堆栈跟踪信息!
That is, all the stack information of the thread executing the method in the code you presented is only in the ExcecutionException, and not in the nested causes, which only cover frames starting at call()
in the Callable. That is, your doSomethingWithTimeout
method won't even appear in the stack traces of the exceptions you are throwing here! You'll only get the disembodied stack from the executor. This is because the ExecutionException
is the only one that was created on the calling thread (see FutureTask.get()
).
也就是说,在您呈现的代码中执行方法的线程的所有堆栈信息仅在ExcecutionException中,而不在嵌套原因中,这些原因仅覆盖Callable中call()处开始的帧。也就是说,你的doSomethingWithTimeout方法甚至不会出现在你抛出的异常的堆栈跟踪中!你只能从执行者那里得到无实体的堆栈。这是因为ExecutionException是在调用线程上创建的唯一一个(请参阅FutureTask.get())。
The only solution I know is complicated. A lot of the problem originates with the liberal exception specification of Callable
- throws Exception
. You can define new variants of Callable
which specify exactly which exceptions they throw, such as:
我所知道的唯一解决方案很复杂。很多问题都源于Callable的*异常规范 - 抛出异常。您可以定义Callable的新变体,它们准确指定它们抛出的异常,例如:
public interface Callable1<T,X extends Exception> extends Callable<T> {
@Override
T call() throws X;
}
This allows methods which executes callables to have a more precise throws
clause. If you want to support signatures with up to N exceptions, you'll need N variants of this interface, unfortunately.
这允许执行callables的方法具有更精确的throws子句。如果您想支持最多N个例外的签名,遗憾的是,您将需要此接口的N个变体。
Now you can write a wrapper around the JDK Executor
which takes the enhanced Callable, and returns an enhanced Future
, something like guava's CheckedFuture. The checked exception type(s) are propagated at compile time from the creation and type of the ExecutorService
, to the returned Future
s, and end up on the getChecked
method on the future.
现在你可以在JDK Executor周围编写一个包装器,它接受增强的Callable,并返回一个增强的Future,比如guava的CheckedFuture。已检查的异常类型在编译时从ExecutorService的创建和类型传播到返回的Futures,并最终在未来的getChecked方法上传播。
That's how you thread the compile-time type safety through. This means that rather than calling:
这就是你通过编译时类型安全的方式。这意味着而不是调用:
Future.get() throws InterruptedException, ExecutionException;
You can call:
你可以打电话:
CheckedFuture.getChecked() throws InterruptedException, ProcessExecutionException, IOException
So the unwrapping problem is avoided - your method immediately throws the exceptions of the required type and they are available and checked at compile time.
因此,避免了解包问题 - 您的方法立即抛出所需类型的异常,并且它们在编译时可用并检查。
Inside getChecked
, however you still need to solve the "missing cause" unwrapping problem described above. You can do this by stitching the current stack (of the calling thread) onto the stack of the thrown exception. This a stretching the usual use of a stack trace in Java, since a single stack stretches across threads, but it works and is easy to understand once you know what is going on.
在getChecked内部,你仍然需要解决上面描述的“缺失原因”展开问题。您可以通过将当前堆栈(调用线程)拼接到抛出异常的堆栈上来完成此操作。这延伸了Java中堆栈跟踪的常规用法,因为单个堆栈跨越线程,但是一旦您知道发生了什么,它就可以工作并且易于理解。
Another option is to create another exception of the same thing as the one being thrown, and set the original as the cause of the new one. You'll get the full stack trace, and the cause relationship will be the same as how it works with ExecutionException
- but you'll have the right type of exception. You'll need to use reflection, however, and is not guaranteed to work, e.g., for objects with no constructor having the usual parameters.
另一种选择是创建与被抛出的异常相同的异常,并将原始异常设置为新原因。您将获得完整的堆栈跟踪,并且原因关系与它与ExecutionException的工作方式相同 - 但您将拥有正确的异常类型。但是,您需要使用反射,并且不能保证工作,例如,对于没有具有常用参数的构造函数的对象。
#2
1
Here are couple of interesting information for checked and against Checked Exceptions. Brian Goetz discussion and an argument of against checked exceptions from Eckel Discussion. But I did not know if you have already implemented and given a thought about the checked exception refactor that is discussed by Joshua in this book.
以下是检查和反对Checked Exceptions的几个有趣信息。 Brian Goetz的讨论和反对Eckel Discussion中检查异常的论点。但我不知道你是否已经实现并考虑了约书亚在本书中讨论的经过检查的异常重构。
According the Effective Java pearls, one of the preferred method of handling Checked exceptions is to turn a checked exception into an Un-Checked Exception. So for example,
根据Effective Java珍珠,处理Checked异常的首选方法之一是将已检查的异常转换为Un-Checked Exception。例如,
try{
obj.someAction()
}catch(CheckedException excep){
}
change this implementation to
将此实现更改为
if(obj.canThisOperationBeperformed){
obj.someAction()
}else{
// Handle the required Exception.
}
#3
1
I'm afraid there's no answer to your problem. Basically, you are launching a task in a different thread than the one you are in, and want to use the ExecutorService pattern to catch all the exceptions that task can throw, plus the bonus of interrupting that task after a certain amount of time. Your approach is the right one : you couldnt do that with a bare Runnable.
我担心你的问题没有答案。基本上,您在与您所在的线程不同的线程中启动任务,并希望使用ExecutorService模式捕获任务可以抛出的所有异常,以及在一定时间后中断该任务的奖励。你的方法是正确的:你无法用裸露的Runnable做到这一点。
And this exception, that you have no information about, you want to throw it again, with a certain type : ProcessExecutionException, InterruptedException or IOException. If it's another type, you want to rethrow it as a RuntimeException (which is btw not the best solution, since you dont cover all the cases).
而这个例外,你没有任何信息,你想再次抛出它,具有某种类型:ProcessExecutionException,InterruptedException或IOException。如果它是另一种类型,你想重新抛出它作为RuntimeException(这不是最好的解决方案,因为你没有涵盖所有的情况)。
So you have an impendance mismatch there : a Throwable on one hand, and a known exception type on the other. The only solution you have to solve it is to do what you've done : check the type, and throw it again with a cast. It can be written differently, but will look the same in the end...
所以你在那里有一个阻碍不匹配:一方面是Throwable,另一方面是一个已知的异常类型。你必须解决它的唯一解决方案是做你已经完成的事情:检查类型,然后再使用强制转换再次抛出它。它可以用不同的方式编写,但最终看起来会一样......
#4
1
Here's what I do in this situation. This accomplishes the following:
这是我在这种情况下所做的。这实现了以下目的:
- Re-throws checked exceptions without wrapping them
- Glues together the stack traces
重新抛出已检查的异常而不包装它们
将堆栈轨迹粘在一起
Code:
public <V> V waitForThingToComplete(Future<V> future) {
boolean interrupted = false;
try {
while (true) {
try {
return future.get();
} catch (InterruptedException e) {
interrupted = true;
}
}
} catch (ExecutionException e) {
final Throwable cause = e.getCause();
this.prependCurrentStackTrace(cause);
throw this.<RuntimeException>maskException(cause);
} catch (CancellationException e) {
throw new RuntimeException("operation was canceled", e);
} finally {
if (interrupted)
Thread.currentThread().interrupt();
}
}
// Prepend stack frames from the current thread onto exception trace
private void prependCurrentStackTrace(Throwable t) {
final StackTraceElement[] innerFrames = t.getStackTrace();
final StackTraceElement[] outerFrames = new Throwable().getStackTrace();
final StackTraceElement[] frames = new StackTraceElement[innerFrames.length + outerFrames.length];
System.arraycopy(innerFrames, 0, frames, 0, innerFrames.length);
frames[innerFrames.length] = new StackTraceElement(this.getClass().getName(),
"<placeholder>", "Changed Threads", -1);
for (int i = 1; i < outerFrames.length; i++)
frames[innerFrames.length + i] = outerFrames[i];
t.setStackTrace(frames);
}
// Checked exception masker
@SuppressWarnings("unchecked")
private <T extends Throwable> T maskException(Throwable t) throws T {
throw (T)t;
}
Seems to work.
似乎工作。
#5
0
I'm not sure why you have the if/else block in the catch and instanceof
, I think you can do what you want with:-
我不确定你为什么在catch和instanceof中有if / else块,我认为你可以做你想要的: -
catch( ProcessExecutionException ex )
{
// handle ProcessExecutionException
}
catch( InterruptException ex )
{
// handler InterruptException*
}
One thing to consider, to reduce clutter, is to catch the exception inside your callable method and re-throw as your own domain/package specific exception or exceptions. How many exceptions you need to create would largely depend on how your calling code will respond to the exception.
要减少混乱,要考虑的一件事是捕获可调用方法中的异常并将其作为自己的域/包特定异常重新抛出。您需要创建多少例外很大程度上取决于您的调用代码将如何响应异常。
#6
0
The javadoc of java.util.concurrent.Future.get()
states the following. Then why not just catch ExecutionException (and Cancellation and Interrupted as declared by the java.util.concurrent.Future.get()
) method?
java.util.concurrent.Future.get()的javadoc声明如下。那么为什么不捕获ExecutionException(以及java.util.concurrent.Future.get()声明的方法取消和中断)?
...
Throws:CancellationException - if the computation was cancelled
CancellationException - 如果计算被取消
ExecutionException - if the computation threw an exception
ExecutionException - 如果计算引发异常
InterruptedException - if the current thread was interrupted while waiting
InterruptedException - 如果当前线程在等待时被中断
So basically you can throw whatever exception within your callable and just catch ExecutionException
. Then ExecutionException.getCause()
will hold the actual exception your callable threw as stated in the javadoc. This way you are shielded from method signature changes related to checked exception declaration.
所以基本上你可以在你的callable中抛出任何异常,然后捕获ExecutionException。然后ExecutionException.getCause()将保存您的callable抛出的实际异常,如javadoc中所述。这样,您就可以屏蔽与已检查异常声明相关的方法签名更改。
By the way you should never catch Throwable
, as this would catch also RuntimeExceptions
and Errors
. Catching Exception
is a little bit better but still not recommended, as it will catch RuntimeExceptions
.
顺便说一下,你永远不应该捕获Throwable,因为这也会捕获RuntimeExceptions和Errors。捕获异常有点好,但仍然不推荐,因为它会捕获RuntimeExceptions。
Something like:
try {
MyResult result = myFutureTask.get();
} catch (ExecutionException e) {
if (errorHandler != null) {
errorHandler.handleExecutionException(e);
}
logger.error(e);
} catch (CancellationException e) {
if (errorHandler != null) {
errorHandler.handleCancelationException(e);
}
logger.error(e);
} catch (InterruptedException e) {
if (errorHandler != null) {
errorHandler.handleInterruptedException(e);
}
logger.error(e);
}
#7
0
In the calling class, catch the Throwable
last. For instance,
在调用类中,最后捕获Throwable。例如,
try{
doSomethingWithTimeout(i);
}
catch(InterruptedException e){
// do something
}
catch(IOException e){
// do something
}
catch(TimeoutException e){
// do something
}
catch(ExecutionException e){
// do something
}
catch(Throwable t){
// do something
}
And the content of doSomethingWithTimeout(int timeout)
should look like this,
doSomethingWithTimeout(int timeout)的内容应该如下所示,
.
.
.
ExecutorService service = Executors.newSingleThreadExecutor();
try {
Future<byte[]> future = service.submit( callable );
return future.get( timeout, TimeUnit.MILLISECONDS );
}
catch(Throwable t){
throw t;
}
finally{
service.shutdown();
}
And it's method signature should look like,
它的方法签名应该是这样的,
doSomethingWithTimeout(int timeout) throws Throwable
doSomethingWithTimeout(int timeout)抛出Throwable
#8
-1
Here is another way to do it, though I'm not convinced that this is less clumsy or less prone to break than to do it with an instanceof check as in your question:
这是另一种方法,虽然我不相信这不像你在问题中那样用笨拙或不太容易破解而不是像检查实例一样:
public static byte[] doSomethingWithTimeout(int timeout)
throws ProcessExecutionException, InterruptedException, IOException, TimeoutException {
....
try {
....
return future.get(1000, TimeUnit.MILLISECONDS);
.....
} catch (ExecutionException e) {
try {
throw e.getCause();
} catch (IOException ioe) {
throw ioe;
} catch (InterruptedException ie) {
throw ie;
} catch (ProcessExecutionException pee) {
throw pee;
} catch (Throwable t) {
//Unhandled exception from Callable endups here
}
} catch (TimeoutException e) {
throw e;
} catch.....
}
#9
-1
I wouldn't say I recommend this, but here is a way you can do it. It is type-safe and whoever comes to modify it after you will probably be unhappy with it.
我不会说我推荐这个,但这是你可以做到的一种方式。它是类型安全的,无论谁来修改它都可能会对它不满意。
public class ConsumerClass {
public static byte[] doSomethingWithTimeout(int timeout)
throws ProcessExecutionException, InterruptedException, IOException, TimeoutException {
MyCallable callable = new MyCallable();
ExecutorService service = Executors.newSingleThreadExecutor();
try {
Future<byte[]> future = service.submit(callable);
return future.get(timeout, TimeUnit.MILLISECONDS);
} catch (ExecutionException e) {
throw callable.rethrow(e);
} finally {
service.shutdown();
}
}
}
// Need to subclass this new callable type to provide the Exception classes.
// This is where users of your API have to pay the price for type-safety.
public class MyCallable extends CallableWithExceptions<byte[], ProcessExecutionException, IOException> {
public MyCallable() {
super(ProcessExecutionException.class, IOException.class);
}
@Override
public byte[] call() throws ProcessExecutionException, IOException {
//Do some work that could throw one of these exceptions
return null;
}
}
// This is the generic implementation. You will need to do some more work
// if you want it to support a number of exception types other than two.
public abstract class CallableWithExceptions<V, E1 extends Exception, E2 extends Exception>
implements Callable<V> {
private Class<E1> e1;
private Class<E2> e2;
public CallableWithExceptions(Class<E1> e1, Class<E2> e2) {
this.e1 = e1;
this.e2 = e2;
}
public abstract V call() throws E1, E2;
// This method always throws, but calling code can throw the result
// from this method to avoid compiler errors.
public RuntimeException rethrow(ExecutionException ee) throws E1, E2 {
Throwable t = ee.getCause();
if (e1.isInstance(t)) {
throw e1.cast(t);
} else if (e2.isInstance(t)) {
throw e2.cast(t);
} else if (t instanceof Error ) {
throw (Error) t;
} else if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new RuntimeException(t);
}
}
}
#10
-2
I've found one way to solve the issue. If it's ExecutionException you can get original one by calling exception.getCause() Then you need to wrap your exception in some kind of Runtime Exception or (what is the best way for me) use @SneakyThrows annotation from project lombok (https://projectlombok.org/). I give a small piece of code example. In addition you can add a few instanceof checks before throwing an exception to be sure this is the one you're expecting.
我找到了解决问题的方法之一。如果它是ExecutionException,你可以通过调用exception.getCause()得到原始的那个然后你需要在某种运行时异常中包装你的异常或(对我来说最好的方法)使用项目lombok中的@SneakyThrows注释(https:// projectlombok.org/)。我给出了一小段代码示例。此外,您可以在抛出异常之前添加一些instanceof检查,以确保这是您期望的那个。
@SneakyThrows
public <T> T submitAndGet(Callable<T> task) {
try {
return executor.submit(task).get(5, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
throw e.getCause();
}
}