Java - 任务和未来 - 我需要捕获异常还是可以将它们留给应用程序线程?

Java - Tasks and Futures - Do I need to catch exceptions or can I leave them to the Application Thread?

我正在使用 JavaFX 编写一个转换器程序,并且正在使用推荐的 javafx.concurrent.Task 来完成远离 JavaFX 应用程序线程的繁重工作。我还在其中使用 java.util.concurrent.Futurejava.util.concurrent.ExecutorService 来完成可以同时完成的额外工作。

然而,很多工作都涉及可能会抛出异常的方法,如果发生这种情况,我需要进程停止运行。我目前正在到处发送 try-catch 块和 return 错误而不是让异常冒出来。

但是因为 call()FutureTask 中都有 throws Exception 声明以防万一有任何未捕获的,我可以不捕获吗Futures 中的异常并让它们在 Task 终止时由应用程序线程处理? 由于异常将执行我想要的并终止线程,同时向应用程序线程提供有关线程停止原因的额外信息,以便我可以显示适当的警报。

我想这样做的另一个原因是在 Futures 中我需要从 Task 线程访问值,但我不需要改变它们所以我想使值 final 并在 lambda 中创建 Futures。 try-catch 块使事情变得复杂,因为我不能像我想要的那样制作尽可能多的值 final(或有效地 final)。这是因为我正在从初始化它们时可能抛出的方法中分配 return 值。因此,我必须将赋值包围在 try-catch 中,因此需要在将临时变量复制到 final 变量之前继续创建临时变量,这会使过程看起来一团糟并且可能会浪费内存。

TaskFuture 中捕获异常是不是一个好主意?在应用程序线程之前不捕获异常是否存在任何重大陷阱或问题?

这是我目前在我的控制器 class 中处理应该触发此过程的事件的示例:

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 class 在另一个线程

中包含对我 运行 的 long-运行ning 逻辑
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 (这通常意味着你仍然可以 return 一个有意义的结果),并让他们如果没有传播。

所以一个 ConverterTask 将一个 String 转换成另一个,可能会抛出阻止转换发生的异常,看起来像

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);

请注意,处理程序(onSucceededonFailed)中的代码是在 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 处理程序)。