我正在使用JavaFX编写一个转换器程序,并且使用推荐的javafx.concurrent.Task
来完成远离JavaFX应用程序线程的繁重工作。我还在其中使用java.util.concurrent.Future
和java.util.concurrent.ExecutorService
来完成可以并发完成的额外工作。
然而,许多工作涉及可能抛出的方法,如果发生这种情况,我需要进程停止。我目前到处乱扔try-catch
块,并返回false,而不是让异常冒泡。
但是,由于Future
和Task
中的call()
都有throws Exception
声明,以防有任何未捕获的声明,我是否可以不捕获期货中的异常,并让它们在Task
终止时由应用程序线程处理?因为异常会做我想要的并终止线程,同时提供额外的信息,关于为什么线程停止到应用程序线程,所以我可以显示一个适当的警报。
我想这样做的另一个原因是在未来我需要访问Task
线程的值,但我不需要改变它们,所以我想使值final
并在lambda中创建未来。try-catch
块使问题复杂化,因为我不能像我想的那样制作尽可能多的final
(或有效的final
)值。这是因为我分配了方法的返回值,这些方法在初始化时可能会抛出。因此,我必须在try-catch
中包围赋值,因此需要在将它们复制到final
变量之前不断创建临时变量,这使得过程看起来很混乱,并且可能浪费内存。
在Task
或Future
中不捕获异常是否是个好主意?是否有任何主要的陷阱或问题不捕捉异常,直到应用程序线程?
这里有一个例子,我目前有什么在我的控制器类来处理事件,应该触发这个过程:
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
转换为另一个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
处理程序)。