嘿,我正在编写一个网络应用程序,在其中我读取了一些自定义二进制格式的数据包。我正在开始一个后台线程来等待传入的数据。问题是,编译器不允许我将任何代码抛出(检查)异常放入run()
。它说:
run() in (...).Listener cannot implement run() in java.lang.Runnable; overridden method does not throw java.io.IOException
我希望异常终止该线程,并让它在父线程中的某处被捕获。这是可能实现还是我必须处理内部线程中的每个异常?
答案 0 :(得分:48)
为了能够将异常发送到父线程,您可以将后台线程放在Callable(它允许抛出已检查的异常)中,然后将其传递给submit some Executor方法{3}}。 submit方法将返回Future,然后您可以使用它来获取异常(其get方法将抛出包含原始异常的ExecutionException。
答案 1 :(得分:38)
警告:如果必须使用异常机制,这可能无法满足您的需求。
如果我理解正确,你实际上并不需要检查异常(你接受了建议未经检查的异常的答案)那么一个简单的监听器模式会更合适吗?
侦听器可以存在于父线程中,当您在子线程中捕获到已检查的异常时,您可以直接通知侦听器。
这意味着你有一种方法可以揭示这种情况会发生(通过公共方法),并且能够传递比异常允许的更多信息。但它确实意味着父线程和子线程之间将存在耦合(尽管是松散的)。这取决于您的具体情况,这是否有利于使用未经检查的异常包装已检查的异常。
这是一个简单的例子(从另一个答案借来的一些代码):
public class ThingRunnable implements Runnable {
private SomeListenerType listener;
// assign listener somewhere
public void run() {
try {
while(iHaveMorePackets()) {
doStuffWithPacket();
}
} catch(Exception e) {
listener.notifyThatDarnedExceptionHappened(...);
}
}
}
耦合来自父线程中的对象必须是SomeListenerType
类型。
答案 2 :(得分:33)
这个答案基于Esko Luontola,但它提供了一个有效的例子。
与Runnable接口的run()方法不同,Callable的call()方法允许抛出一些异常。这是一个实现示例:
public class MyTask implements Callable<Integer> {
private int numerator;
private int denominator;
public MyTask(int n, int d) {
this.numerator = n;
this.denominator = d;
}
@Override
// The call method may throw an exception
public Integer call() throws Exception {
Thread.sleep(1000);
if (denominator == 0) {
throw new Exception("cannot devide by zero");
} else {
return numerator / denominator;
}
}
}
Executor提供了一种在线程内运行Callable并处理任何异常的机制:
public class Main {
public static void main(String[] args) {
// Build a task and an executor
MyTask task = new MyTask(2, 0);
ExecutorService threadExecutor = Executors.newSingleThreadExecutor();
try {
// Start task on another thread
Future<Integer> futureResult = threadExecutor.submit(task);
// While task is running you can do asynchronous operations
System.out.println("Something that doesn't need the tasks result");
// Now wait until the result is available
int result = futureResult.get();
System.out.println("The result is " + result);
} catch (ExecutionException e) {
// Handle the exception thrown by the child thread
if (e.getMessage().contains("cannot devide by zero"))
System.out.println("error in child thread caused by zero division");
} catch (InterruptedException e) {
// This exception is thrown if the child thread is interrupted.
e.printStackTrace();
}
}
}
答案 3 :(得分:7)
我所做的是捕获线程中的异常并将其存储为Runnable的成员变量。然后通过Runnable上的getter公开此异常。然后,我扫描父项中的所有线程,看看是否有异常,并采取适当的措施。
答案 4 :(得分:3)
如果在引发异常时你真的无法做任何有用的事情,你可以在RuntimeException中包装已检查的异常。
try {
// stuff
} catch (CheckedException yourCheckedException) {
throw new RuntimeException("Something to explain what is happening", yourCheckedException);
}
答案 5 :(得分:3)
线程不能将异常抛出到任何其他线程(也不是主线程)。并且你不能使继承的run()方法抛出任何已检查的异常,因为你只能抛出少于继承的代码,而不是更多。
答案 6 :(得分:1)
如果您的线程代码抛出RuntimeExpection,则不需要添加run()throw Exception。
但是只在适当的时候使用此解决方案,因为这可能是一个糟糕的实践: http://java.sun.com/docs/books/tutorial/essential/exceptions/runtime.html
任何RuntimeException或未经检查的异常都可以帮到您。也许你需要创建自己的RuntimeException
答案 7 :(得分:0)
假设您的代码处于某种循环中,您可以写:
public class ThingRunnable implements Runnable {
public void run() {
try {
while(iHaveMorePackets()) {
doStuffWithPacket()
}
} catch(Exception e) {
System.out.println("Runnable terminating with exception" + e );
}
}
}
异常将自动阻止你退出循环,并且在run()方法结束时,线程将停止。
答案 8 :(得分:0)
使用这个 Runnable 创建你的线程:
public abstract class TryRunner implements Runnable{
protected abstract void tryToRun();
protected void onException(Exception e){}
@Override
final public void run() {
try{ tryToRun(); }catch(Exception e){ e.printStackTrace(); onException(e); }
}
}
答案 9 :(得分:-1)
将您的异常包含在RuntimeException
内似乎可以解决问题。
someMethod() throws IOException
{
try
{
new Thread(() ->
{
try
{
throw new IOException("a checked exception thrown from within a running thread");
}
catch(IOException ex)
{
throw new RuntimeException("a wrapper exception", ex); // wrap the checked exception inside an unchecked exception and throw it
}
}).start();
}
catch(RuntimeException ex) // catch the wrapped exception sent from within the thread
{
if(ex.getCause() instanceof IOException)
throw ex.getCause; // unwrap the checked exception using getCause method and use it however you need
else
throw ex;
}
}