您可以使用Callable
,将其提交给一个ExecutorService
,并通过ExecutorService.submit()
方法等待结果,使用FutureTask.isDone()
来判断是否已完成。
当isDone()
返回true时,调用FutureTask.get()
获取结果。如果您的Callable
抛出了一个Exception
,那么FutureTask.get()
也会抛出一个Exception
,而您可以通过Exception.getCause()
访问原始的异常信息。
如果你想将实现Runnable
接口的类传递到Thread
框架中,那么你必须遵守该框架的规则。请参考Ernest Friedman-Hill的回答,了解为什么以其他方式做这件事是不明智的。
不过我猜你想在你的代码中直接调用run
方法,以便你的调用代码可以处理异常。
解决这个问题很容易。不要使用Thread库中的Runnable
接口,而是创建一个具有修改后签名的自己的接口,允许抛出已检查异常,例如:
public interface MyRunnable
{
void myRun ( ) throws MyException;
}
你甚至可以创建一个适配器来将此接口转换为适合在 Thread 框架中使用的真正的 Runnable
(通过处理已检查异常)。
Runnable
只是一个简单的接口,我们可以自己定义。虽然对于线程使用场景并不太有用,但对于传递不同的“可运行代码块”,它非常完美。 - Richard Le Mesurierrun()
抛出了一个已检查的异常,那么谁来捕获它呢?你无法将该run()
调用放在一个处理程序中,因为你不编写调用它的代码。run()
方法中捕获你的已检查异常,并抛出一个未经检查的异常(即RuntimeException
)代替。这将以堆栈跟踪的形式终止线程,也许这正是你想要的结果。run()
方法在某个地方报告错误,那么你只需为run()
方法的catch
块提供一个回调方法;该方法可以在某个位置存储异常对象,然后你感兴趣的线程就可以在那个位置找到该对象。main()
抛出了一个已检查的异常,那么谁会捕获它?”“如果run()
抛出了一个未检查的异常,那么谁会捕获它?” - Christian Hujer是的,有一种方法可以从run()
方法抛出一个checked异常,但它非常糟糕,我不会分享它。
相反,您可以使用与运行时异常相同的机制来执行以下操作:
@Override
public void run() {
try {
/* Do your thing. */
...
} catch (Exception ex) {
Thread t = Thread.currentThread();
t.getUncaughtExceptionHandler().uncaughtException(t, ex);
}
}
run()
方法确实是Thread
的目标,那么抛出异常是没有意义的,因为它是不可观察的;抛出异常与不抛出异常(没有)具有相同的效果。Thread
的目标,请不要使用Runnable
。例如,也许Callable
更合适。t.getUncaughtExceptionHandler().uncaughtException(t, ex);
将其抛出到测试用例中。添加这一行代码会导致进程崩溃!不知道为什么。 - DineshThread.getDefaultUncaughtExceptionHandler()
返回null
吗?如果不是,结果的类型是什么?如果你没有调用uncaughtException()
而是将已检查的异常放入RuntimeException
中并抛出会发生什么? - ericksonRuntimeException
也会导致应用程序崩溃。 - DineshThread.getDefaultUncaughtExceptionHandler()
是否返回 null
;如果不是,则 Android 提供了一个默认值,以提供报告等功能。但是您可以将其设置为执行您想要的操作。更多信息请参见此处。 - erickson@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;
}
Runnable
的情况,而且在Java 8中,在引入功能接口而没有处理已检查异常的可能性的上下文中,也经常出现。例如,Consumer
、Supplier
、Function
、BiFunction
等都声明了不能处理已检查异常的功能接口。Runnable
代表任何未声明异常或声明异常过于有限以致无法处理的功能接口。Runnable
,可以用其他东西替换Runnable
。Runnable
替换为Callable<Void>
。基本相同,但允许抛出异常;并且最终必须返回null
,这是一种轻微的麻烦。
- 考虑用自己的自定义@FunctionalInterface
替换Runnable
,该接口可以抛出你想要的异常。Callable<Void>
代替Runnable
。RuntimeException
中。
- 通过使用未检查的转换,你可以将异常强制转换为RuntimeException。@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 Throwable(t)
,因为它的堆栈跟踪更短。
现在你可以这样做:
executorService.submit((ThrowingRunnable) () -> {throw new Exception()});
免责声明:在未来版本的Java中,当泛型类型信息不仅在编译时而且在运行时也被处理时,以这种方式执行未经检查的转换的能力可能会被删除。
是的,你可以从run()方法中抛出已检查异常。通过欺骗编译器,可以使用泛型来完成这个操作。看一下这段代码:
public static void main(String[] args) {
new Main().throwException();
}
public void throwException() {
Runnable runnable = () -> throwAs(new Exception());
new Thread(runnable).start();
}
private <T extends Throwable> void throwAs(Throwable t) throws T {
throw ( T ) t;
}
如果你想从可运行的run()方法中抛出已检查异常,这可能会有所帮助
你的需求没有任何意义。如果你想通知线程调用者发生了异常,你可以通过回调机制来实现。这可以通过Handler、广播或其他你能想到的方式来实现。
最简单的方法是定义自己的异常对象,它继承了RuntimeException
类而不是Exception
类。