- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
我有一个方法可以超时执行某些任务。我使用 ExecutorServer.submit() 来获取 Future 对象,然后我调用 future.get() 并超时。这工作正常,但我的问题是处理我的任务可能引发的已检查异常的最佳方法。以下代码有效,并保留了已检查的异常,但如果方法签名中的已检查异常列表发生更改,它似乎非常笨拙且容易中断。
关于如何解决此问题的任何建议?我需要以 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 );
}
}
}
=== 更新 ===
许多人发布的回复建议 1) 作为一般异常重新抛出,或 2) 作为未经检查的异常重新抛出。我不想做这些,因为这些异常类型(ProcessExecutionException、InterruptedException、IOException、TimeoutException)很重要——它们每个都将被处理的调用以不同的方式处理。如果我不需要超时功能,那么我希望我的方法抛出这 4 种特定的异常类型(好吧,TimeoutException 除外)。我不认为添加超时功能应该将我的方法签名更改为抛出通用异常类型。
最佳答案
我已经深入研究过这个问题,结果一团糟。在 Java 5、6 或 7 中都没有简单的答案。除了您指出的笨拙、冗长和脆弱之外,您的解决方案实际上还有一个问题,即您正在剥离的 ExecutionException
当你调用 getCause()
时实际上包含了大部分重要的堆栈跟踪信息!
也就是说,你提供的代码中执行方法的线程的所有堆栈信息都只在ExecutionException中,而不在嵌套原因中,它只涵盖从call()
开始的帧在可调用。也就是说,您的 doSomethingWithTimeout
方法甚至不会出现在您在这里抛出的异常的堆栈跟踪中!您只会从 executor 那里获得无实体的堆栈。这是因为 ExecutionException
是唯一在调用线程上创建的(参见 FutureTask.get()
)。
我知道的唯一解决方案很复杂。很多问题都源于 Callable
- throws Exception
的自由异常规范。您可以定义 Callable
的新变体,准确指定它们抛出的异常,例如:
public interface Callable1<T,X extends Exception> extends Callable<T> {
@Override
T call() throws X;
}
这允许执行可调用对象的方法具有更精确的 throws
子句。如果您想支持最多包含 N 个异常的签名,那么很遗憾,您将需要此接口(interface)的 N 个变体。
现在您可以在JDK Executor
周围编写一个包装器,它采用增强的Callable,并返回一个增强的Future
,类似于guava 的CheckedFuture .检查的异常类型在编译时从 ExecutorService
的创建和类型传播到返回的 Future
,并最终在 getChecked
关于 future 的方法。
这就是您如何通过线程来实现编译时类型安全。这意味着,而不是调用:
Future.get() throws InterruptedException, ExecutionException;
您可以调用:
CheckedFuture.getChecked() throws InterruptedException, ProcessExecutionException, IOException
因此避免了展开问题 - 您的方法会立即抛出所需类型的异常,并且它们在编译时可用和检查。
在 getChecked
内部,但是您仍然需要解决上述“缺失原因”展开问题。您可以通过将当前堆栈(调用线程的)拼接到抛出异常的堆栈上来做到这一点。这是扩展 Java 中堆栈跟踪的常用用法,因为单个堆栈跨越线程,但是一旦您知道发生了什么,它就可以工作并且很容易理解。
另一种选择是创建 another 与被抛出的相同事物的异常,并将原始异常设置为新异常的原因。您将获得完整的堆栈跟踪,原因关系将与它与 ExecutionException
的工作方式相同 - 但您将拥有正确的异常类型。但是,您需要使用反射,并且不能保证可以正常工作,例如,对于没有具有常用参数的构造函数的对象。
关于java - 处理 ExecutionException 的最佳方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10437890/
我想了解 Ruby 方法 methods() 是如何工作的。 我尝试使用“ruby 方法”在 Google 上搜索,但这不是我需要的。 我也看过 ruby-doc.org,但我没有找到这种方法。
Test 方法 对指定的字符串执行一个正则表达式搜索,并返回一个 Boolean 值指示是否找到匹配的模式。 object.Test(string) 参数 object 必选项。总是一个
Replace 方法 替换在正则表达式查找中找到的文本。 object.Replace(string1, string2) 参数 object 必选项。总是一个 RegExp 对象的名称。
Raise 方法 生成运行时错误 object.Raise(number, source, description, helpfile, helpcontext) 参数 object 应为
Execute 方法 对指定的字符串执行正则表达式搜索。 object.Execute(string) 参数 object 必选项。总是一个 RegExp 对象的名称。 string
Clear 方法 清除 Err 对象的所有属性设置。 object.Clear object 应为 Err 对象的名称。 说明 在错误处理后,使用 Clear 显式地清除 Err 对象。此
CopyFile 方法 将一个或多个文件从某位置复制到另一位置。 object.CopyFile source, destination[, overwrite] 参数 object 必选
Copy 方法 将指定的文件或文件夹从某位置复制到另一位置。 object.Copy destination[, overwrite] 参数 object 必选项。应为 File 或 F
Close 方法 关闭打开的 TextStream 文件。 object.Close object 应为 TextStream 对象的名称。 说明 下面例子举例说明如何使用 Close 方
BuildPath 方法 向现有路径后添加名称。 object.BuildPath(path, name) 参数 object 必选项。应为 FileSystemObject 对象的名称
GetFolder 方法 返回与指定的路径中某文件夹相应的 Folder 对象。 object.GetFolder(folderspec) 参数 object 必选项。应为 FileSy
GetFileName 方法 返回指定路径(不是指定驱动器路径部分)的最后一个文件或文件夹。 object.GetFileName(pathspec) 参数 object 必选项。应为
GetFile 方法 返回与指定路径中某文件相应的 File 对象。 object.GetFile(filespec) 参数 object 必选项。应为 FileSystemObject
GetExtensionName 方法 返回字符串,该字符串包含路径最后一个组成部分的扩展名。 object.GetExtensionName(path) 参数 object 必选项。应
GetDriveName 方法 返回包含指定路径中驱动器名的字符串。 object.GetDriveName(path) 参数 object 必选项。应为 FileSystemObjec
GetDrive 方法 返回与指定的路径中驱动器相对应的 Drive 对象。 object.GetDrive drivespec 参数 object 必选项。应为 FileSystemO
GetBaseName 方法 返回字符串,其中包含文件的基本名 (不带扩展名), 或者提供的路径说明中的文件夹。 object.GetBaseName(path) 参数 object 必
GetAbsolutePathName 方法 从提供的指定路径中返回完整且含义明确的路径。 object.GetAbsolutePathName(pathspec) 参数 object
FolderExists 方法 如果指定的文件夹存在,则返回 True;否则返回 False。 object.FolderExists(folderspec) 参数 object 必选项
FileExists 方法 如果指定的文件存在返回 True;否则返回 False。 object.FileExists(filespec) 参数 object 必选项。应为 FileS
我是一名优秀的程序员,十分优秀!