嘿,我正在编写一个网络应用程序,我在其中读取一些自定义二进制格式的数据包。我正在启动一个后台线程来等待传入的数据。问题是,编译器不允许我将任何抛出(检查)异常的代码放入run()
. 它说:
(...).Listener 中的 run() 无法实现 java.lang.Runnable 中的 run();被覆盖的方法不会抛出 java.io.IOException
我希望异常杀死线程,并让它在父线程的某个地方被捕获。这可能实现还是我必须处理线程内的每个异常?
嘿,我正在编写一个网络应用程序,我在其中读取一些自定义二进制格式的数据包。我正在启动一个后台线程来等待传入的数据。问题是,编译器不允许我将任何抛出(检查)异常的代码放入run()
. 它说:
(...).Listener 中的 run() 无法实现 java.lang.Runnable 中的 run();被覆盖的方法不会抛出 java.io.IOException
我希望异常杀死线程,并让它在父线程的某个地方被捕获。这可能实现还是我必须处理线程内的每个异常?
为了能够将异常发送到父线程,您可以将后台线程放在Callable中(它还允许抛出已检查的异常),然后将其传递给某些 Executor的submit方法。submit 方法将返回一个Future,然后您可以使用它来获取异常(它的get方法将抛出一个包含原始异常的ExecutionException )。
警告:如果您必须使用异常机制,这可能无法满足您的需求。
如果我对您的理解正确,您实际上不需要检查异常(您已经接受了建议未检查异常的答案),那么简单的侦听器模式是否更合适?
侦听器可以存在于父线程中,当您在子线程中捕获已检查的异常时,您可以简单地通知侦听器。
这意味着您有一种方法可以揭示这将发生(通过公共方法),并且能够传递比异常允许的更多的信息。但这确实意味着父线程和子线程之间会有一个耦合(尽管是松散的)。这取决于您的具体情况,这是否比用未经检查的异常包装检查的异常更有好处。
这是一个简单的例子(一些代码从另一个答案借来):
public class ThingRunnable implements Runnable {
private SomeListenerType listener;
// assign listener somewhere
public void run() {
try {
while(iHaveMorePackets()) {
doStuffWithPacket();
}
} catch(Exception e) {
listener.notifyThatDarnedExceptionHappened(...);
}
}
}
耦合来自父线程中必须是 type 的对象SomeListenerType
。
此答案基于 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();
}
}
}
我所做的是在线程中捕获异常并将其存储为 Runnable 的成员变量。然后通过 Runnable 上的 getter 公开此异常。然后我扫描来自父级的所有线程以查看是否有异常,并采取适当的措施。
如果在引发异常时您真的无法做任何有用的事情,您可以将检查的异常包装在 RuntimeException 中。
try {
// stuff
} catch (CheckedException yourCheckedException) {
throw new RuntimeException("Something to explain what is happening", yourCheckedException);
}
该线程不能将异常抛给任何其他线程(也不能抛给主线程)。并且您不能使继承的 run() 方法抛出任何已检查的异常,因为您只能抛出少于继承的代码,而不是更多。
如果你的线程代码抛出了 RuntimeExpection,你不需要添加 run() throw Exception。
但是只有在适当的时候才使用这个解决方案,因为这可能是一个不好的做法:http: //java.sun.com/docs/books/tutorial/essential/exceptions/runtime.html
任何 RuntimeException 或未经检查的异常都可以帮助您。也许您需要创建自己的 RuntimeException
假设您的代码处于某种循环中,您将编写:
public class ThingRunnable implements Runnable {
public void run() {
try {
while(iHaveMorePackets()) {
doStuffWithPacket()
}
} catch(Exception e) {
System.out.println("Runnable terminating with exception" + e );
}
}
}
异常会自动让您跳出循环,并且在 run() 方法结束时,线程将停止。
使用此 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); }
}
}
将异常包装在 aRuntimeException
中似乎可以解决问题。
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;
}
}