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

标签 java multithreading exception concurrency javafx-8

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

但是,很多工作涉及可能会抛出异常的方法,如果发生这种情况,我需要该进程停止运行。我目前正在到处发送 try-catch block 并返回 false,而不是让异常冒泡。

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

我想要这样做的另一个原因是在 Future 中,我需要从 Task 线程访问值,但我不需要更改它们,所以我想将值设置为 final 并在 lambda 中创建 Future。 try-catch block 使事情变得复杂,因为我无法按照我的意愿创建尽可能多的值 final (或实际上 final)。这是因为我正在分配初始化方法时可能会抛出异常的方法的返回值。因此,我必须将赋值放在 try-catch 中,因此需要在将临时变量复制到 final 变量之前不断创建临时变量,这使得过程看起来一团糟,并且可能浪费内存。

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

这是我当前在 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 转换为另一个 StringConverterTask 可能会引发异常,从而阻止转换发生,如下所示

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 处理程序)。

关于Java - 任务和 future - 我需要捕获异常还是可以将它们留给应用程序线程?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31705253/

相关文章:

exception - Hadoop InvalidInput异常

python - 使用多线程时如何正确处理此错误

java - Weblogic 12.1.3 - CXF 3.x - REST - 部署错误

java - 如何在java中写入和读取pgm p5文件

linux - 如何在 linux 的 openmpi 中启用多线程标志?

java - 线程中的方法调用给出 "Can' t 在线程内创建处理程序“异常

java - 在 REST API 响应中返回登录凭据的最佳实践是什么?

java - 如何逐步计算字符串数组中的字符数? -Java

multithreading - Matlabpool 线程数与核心数

c# - 有什么方法可以确定在 C# 中等待锁定的线程数?