有没有办法让Runnable的run()抛出异常?


80

我在实现Runnable的类中的run()中调用的方法被设计为引发异常。

但是Java编译器不允许我这样做,建议我用try / catch包围它。

问题是,通过用try / catch包围它,我使特定的 run()变得无用。我确实想抛出该异常。

如果我指定throwsrun()的本身,编译器会抱怨说Exception is not compatible with throws clause in Runnable.run()

通常我完全不放过 run()抛出异常的。但是,在我必须具有该功能的特殊情况下。

如何解决此限制?


除了其他答案,要跟踪任务的进度,您可以使用FutureTask类。
JProgrammer

Answers:


23

如果你想传递一个类,它实现RunnableThread框架,那么你必须通过该框架的规则玩,见欧内斯特·弗里德曼-希尔的回答,为什么这样做,否则是一个坏主意。

不过,我有一种直觉,您想run直接在代码中调用方法,以便您的调用代码可以处理异常。

这个问题的答案很简单。不要使用RunnableThread库中的接口,而是使用修改后的签名创建自己的接口,该签名允许抛出被检查的异常,例如

public interface MyRunnable
{
    void myRun ( ) throws MyException;
}

您甚至可以创建一个适配器,将该适配器转换为Runnable适合在Thread框架中使用的真实接口(通过处理检查到的异常)。


如此简单的解决方案就是不考虑“盒子里的东西”。当然,aRunnable只是一个简单的界面,我们可以自己做一个。对于线程用例没有用,但是对于传递不同的“可运行”代码块来说,这是完美的。
理查德·勒·马修里尔

82

您可以Callable改为使用,将其提交到,ExecutorService然后等待结果 FutureTask.isDone()返回ExecutorService.submit()

isDone()返回true时,您致电FutureTask.get()。现在,如果你Callable已经抛出Exception,然后FutureTask.get()家业抛出Exception也和原来的异常,您将使用能够访问Exception.getCause()


23

如果run()抛出了检查异常,将会捕获什么?您无法将该run()调用包含在处理程序中,因为您没有编写调用它的代码。

您可以在run()方法中捕获已检查的异常,并RuntimeException在其位置抛出未检查的异常(即)。这将使用堆栈跟踪终止线程;也许那就是你所追求的。

相反,如果您希望您的run()方法在某处报告错误,则可以只提供一个回调方法供该run()方法的catch块调用;该方法可以将异常对象存储在某个位置,然后您感兴趣的线程可以在该位置找到该对象。


2
第一部分不是一个好的论点。“如果main()抛出了一个受检查的异常,将会捕获到什么?” “如果run()抛出了一个未经检查的异常,将会捕获到什么?”
Christian Hujer

16

是的,有一种方法可以从该方法中引发一个已检查的异常run(),但这太可怕了,我不会共享它。

您可以改用以下方法:它使用与运行时异常相同的机制:

@Override
public void run() {
  try {
    /* Do your thing. */
    ...
  } catch (Exception ex) {
    Thread t = Thread.currentThread();
    t.getUncaughtExceptionHandler().uncaughtException(t, ex);
  }
}

正如其他人指出的那样,如果您的run()方法确实是a的目标Thread,则抛出异常是没有意义的,因为它是不可观察的。引发异常与不引发异常(无)具有相同的效果。

如果不是Thread目标,请不要使用Runnable。例如,也许Callable更合适。


但这会导致进程在抛出时崩溃吗?
Dinesh

@DineshVG不,只有JVM中的错误才能导致真正的崩溃。默认的异常处理程序仅打印异常。如果您习惯在那之后看到进程退出,那是因为该线程是唯一运行的线程,并且终止了。
erickson

我尝试(在android工具测试用例中)将其用于soap调用,如果从Soap调用中得到400,则抛出异常。启动测试用例时,从线程调用此肥皂调用。该线程使用此线程将t.getUncaughtExceptionHandler().uncaughtException(t, ex);其扔到工具测试用例。添加这一行会导致进程崩溃!不知道为什么。
Dinesh

@DineshVG在那种环境下,Thread.getDefaultUncaughtExceptionHandler()返回null吗?如果不是,结果是什么类型?如果uncaughtException()将选中的异常包装在a中RuntimeException并抛出该异常,而不是调用,会发生什么情况?
erickson

1
@DineshVG Android可能正在设置执行此操作的默认未捕获异常处理程序。这就是为什么我问是否Thread.getDefaultUncaughtExceptionHandler()退货null;如果没有,则Android将提供默认值,以便提供报告等。但是您可以将其设置为执行所需的操作。这里有更多信息
erickson

6
@FunctionalInterface
public interface CheckedRunnable<E extends Exception> extends Runnable {

    @Override
    default void run() throws RuntimeException {
        try {
            runThrows();
        }
        catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    void runThrows() throws E;

}

1

有些人试图说服您必须遵守规则。听,但是无论您是否服从,都应根据自己的情况决定自己。现实是“您应该遵守规则”(而不是“您必须遵守规则”)。请注意,如果您不遵守规则,可能会造成后果。

这种情况不仅适用于的情况Runnable,而且在Java 8中也经常在Streams和其他引入了功能接口而无法处理受检查的异常的地方使用。例如ConsumerSupplierFunctionBiFunction等等都没有设施,以应付检查的异常被宣布。

那么情况和选择是什么?在下面的文字中,Runnable表示没有声明异常或声明异常对手边用例而言有限的任何功能接口。

  1. 您已经声明了Runnable自己的位置,并且可以Runnable用其他内容代替。
    1. 考虑更换RunnableCallable<Void>。基本上是一样的,但是允许抛出异常;并且必须return null最后,这是一个轻微的烦恼。
    2. 考虑用Runnable您自己的自定义替换,@FunctionalInterface该自定义可以完全抛出所需的异常。
  2. 您已经使用了API,并且可以使用其他替代方法。例如,某些Java API重载,因此您可以使用Callable<Void>代替Runnable
  3. 您已经使用了API,没有其他选择。在那种情况下,您仍然没有选择余地。
    1. 您可以在中包装例外RuntimeException
    2. 您可以通过使用未经检查的强制转换将异常引入RuntimeException中。

您可以尝试以下方法。这有点hack,但是有时候我们需要hack。因为,是否应该检查异常是由其类型定义的,但实际上实际上应该由情况来定义。

@FunctionalInterface
public interface ThrowingRunnable extends Runnable {
    @Override
    default void run() {
        try {
            tryRun();
        } catch (final Throwable t) {
            throwUnchecked(t);
        }
    }

    private static <E extends RuntimeException> void throwUnchecked(Throwable t) {
        throw (E) t;
    }

    void tryRun() throws Throwable;
}

我更喜欢此方法,new RuntimeException(t)因为它具有较短的堆栈跟踪。

您现在可以执行以下操作:

executorService.submit((ThrowingRunnable) () -> {throw new Exception()});

免责声明:当泛型类型信息不仅在编译时而且在运行时都处理时,在Java的将来版本中实际上可能会取消以这种方式执行未经检查的强制转换的功能。


0

您的要求没有任何意义。如果您想将发生的异常通知线程的被调用者,则可以通过回调机制来实现。这可以通过处理程序,广播或其他您能想到的方法来实现。


0

我认为侦听器模式可能会帮助您解决这种情况。如果您的run()方法中发生异常,请使用try-catch块,并在catch中发送异常事件的通知。然后处理您的通知事件。我认为这将是一种更清洁的方法。该SO链接为您提供了指向该方向的有用指导。


-1

最简单的方法是定义自己的异常对象,该对象扩展RuntimeException类而不是Exception类。


亲爱的先生,当发生RuntimeException时,您将如何获得它?
睡鼠

仅此一点并不能回答问题。
卡尔·里希特'18
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.