我正在使用 JavaFX 编写转换器程序,并使用推荐的 javafx.concurrent.Task
来完成 JavaFX 应用程序线程之外的繁重工作。我还在其中使用 java.util.concurrent.Future 和 java.util.concurrent.ExecutorService 来完成可以同时完成的额外工作。
但是,很多工作涉及可能会抛出异常的方法,如果发生这种情况,我需要该进程停止运行。我目前正在到处发送 try-catch
block 并返回 false,而不是让异常冒泡。
但是,由于 Future
和 Task
中的 call()
都有 throws Exception
声明,以防万一有任何未捕获的异常,我可以不捕获 Future 中的异常并在 Task
终止时让应用程序线程处理它们吗?
因为异常将执行我想要的操作并终止线程,同时提供有关线程为何停止到应用程序线程的额外信息,以便我可以显示适当的警报。
我想要这样做的另一个原因是在 Future 中,我需要从 Task
线程访问值,但我不需要更改它们,所以我想将值设置为 final
并在 lambda 中创建 Future。 try-catch
block 使事情变得复杂,因为我无法按照我的意愿创建尽可能多的值 final
(或实际上 final
)。这是因为我正在分配初始化方法时可能会抛出异常的方法的返回值。因此,我必须将赋值放在 try-catch
中,因此需要在将临时变量复制到 final
变量之前不断创建临时变量,这使得过程看起来一团糟,并且可能浪费内存。
不在 Task
或 Future
中捕获异常是不是一个好主意?在应用程序线程之前不捕获异常是否存在任何重大陷阱或问题?
这是我当前在 Controller 类中处理应触发此过程的事件的示例:
ExecutorService converterExecutor = Executors.newSingleThreadExecutor();
ConverterTask converterThread = new ConverterTask()
converterExecutor.execute(converterThread);
Boolean success = null;
try
{
success = converterThread.get();
}
catch (InterruptedException | ExecutionException e1)
{
e1.printStackTrace();
return false;
}
if (success == null)
{
return false;
}
包含长时间运行的逻辑的 ConverterTask
类在另一个线程中运行
public class ConverterTask extends Task< Boolean >
{
public Boolean call() throws Exception
{
//do stuff
ExecutorService executor = Executors.newSingleThreadExecutor();
Future< String > skeletonThread = executor.submit(new Callable< String >()
{
//stuff that can throw exceptions
});
//do more stuff
String temp_sklData = null;
try
{
temp_sklData = skeletonThread.get();
}
catch (InterruptedException | ExecutionException e1)
{
e1.printStackTrace();
return false;
}
if (temp_sklData == null)
{
return false;
}
final String sklData = temp_sklData;
//do more stuff including use sklData in a Lambda Future
}
}
如果传播异常是个好主意,我想在 converterTask
中做什么
public class converterTask extends Task< Boolean >
{
public Boolean call() throws Exception
{
//do stuff
ExecutorService executor = Executors.newSingleThreadExecutor();
Future< String > skeletonThread = executor.submit(new Callable< String >()
{
//stuff that can throw exceptions
});
//do more stuff
final String sklData = skeletonThread.get();
//do more stuff including use sklData in a Lambda Future
}
}
最佳答案
由于其他人在评论中指出的原因,我不太理解您的代码结构。但您的问题的基本答案是,如果您可以优雅地处理异常(这通常意味着您仍然可以返回有意义的结果),则应该使用 try
-catch
,如果不能,则让它们传播。
因此,将一个 String
转换为另一个 String
的 ConverterTask
可能会引发异常,从而阻止转换发生,如下所示
public class ConverterTask extends Task<String> {
private final String textToConvert ;
public ConverterTask(String textToConvert) {
this.textToConvert = textToConvert ;
}
@Override
public String call() throws Exception {
String result = doConversion(textToConvert); // may throw exception...
return result ;
}
}
典型用法是
Executor conversionExec = ... ;
// ...
ConverterTask converterTask = new ConverterTask(someText);
converterTask.setOnSucceeded(e -> {
String result = converterTask.getValue();
// process result...
});
converterTask.setOnFailed(e -> {
// code to execute in case of unhandled exception...
// note you can get the exception with
Throwable thingThatWentWrong = converterTask.getException();
// ...
});
conversionExec.execute(converterTask);
请注意,任一处理程序(onSucceeded
或 onFailed
)中的代码都会在 FX 应用程序线程上为您执行,因此您不应阻塞这些方法,但您可以访问 UI 元素。
如果你遇到了可以恢复的异常,你会做一些类似的事情
@Override
public String call() {
try {
String result = doConversion(textToConvert);
return result ;
} catch (Exception e) {
// fallback computation that doesn't throw exception...
String result = doSafeConversion(textToConvert);
return result ;
}
}
然后您就不需要 onFailed
处理程序。
显然,如果您有一些可恢复的异常和一些不可恢复的异常,您可以结合使用这两种技术:
@Override
public String call() throws Exception {
String partialResult ;
// recoverable:
try {
partialResult = doSomeProcessing(textToConvert);
} catch (Exception e) {
partialResult = computeFallbackValue(textToConvert);
}
// non-recoverable: may throw exception which is propagated out
String result = completeComputation(partialResult);
return result ;
}
(现在您再次需要 onFailed
处理程序)。
关于Java - 任务和 future - 我需要捕获异常还是可以将它们留给应用程序线程?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31705253/