43

嘿,我正在编写一个网络应用程序,我在其中读取一些自定义二进制格式的数据包。我正在启动一个后台线程来等待传入的数据。问题是,编译器不允许我将任何抛出(检查)异常的代码放入run(). 它说:

(...).Listener 中的 run() 无法实现 java.lang.Runnable 中的 run();被覆盖的方法不会抛出 java.io.IOException

我希望异常杀死线程,并让它在父线程的某个地方被捕获。这可能实现还是我必须处理线程内的每个异常?

4

10 回答 10

50

为了能够将异常发送到父线程,您可以将后台线程放在Callable中(它还允许抛出已检查的异常),然后将其传递给某些 Executor的submit方法。submit 方法将返回一个Future,然后您可以使用它来获取异常(它的get方法将抛出一个包含原始异常的ExecutionException )。

于 2009-09-02T20:19:38.510 回答
42

警告:如果您必须使用异常机制,这可能无法满足您的需求。

如果我对您的理解正确,您实际上不需要检查异常(您已经接受了建议未检查异常的答案),那么简单的侦听器模式是否更合适?

侦听器可以存在于父线程中,当您在子线程中捕获已检查的异常时,您可以简单地通知侦听器。

这意味着您有一种方法可以揭示这将发生(通过公共方法),并且能够传递比异常允许的更多的信息。但这确实意味着父线程和子线程之间会有一个耦合(尽管是松散的)。这取决于您的具体情况,这是否比用未经检查的异常包装检查的异常更有好处。

这是一个简单的例子(一些代码从另一个答案借来):

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

于 2009-09-02T20:03:57.107 回答
37

此答案基于 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();
        }
    }
}
于 2011-09-24T13:24:11.103 回答
7

我所做的是在线程中捕获异常并将其存储为 Runnable 的成员变量。然后通过 Runnable 上的 getter 公开此异常。然后我扫描来自父级的所有线程以查看是否有异常,并采取适当的措施。

于 2009-09-02T19:37:45.287 回答
5

如果在引发异常时您真的无法做任何有用的事情,您可以将检查的异常包装在 RuntimeException 中。

try {
    // stuff
} catch (CheckedException yourCheckedException) {
    throw new RuntimeException("Something to explain what is happening", yourCheckedException);
}
于 2009-09-02T18:03:03.903 回答
3

该线程不能将异常抛给任何其他线程(也不能抛给主线程)。并且您不能使继承的 run() 方法抛出任何已检查的异常,因为您只能抛出少于继承的代码,而不是更多。

于 2009-09-02T18:06:09.483 回答
1

如果你的线程代码抛出了 RuntimeExpection,你不需要添加 run() throw Exception。

但是只有在适当的时候才使用这个解决方案,因为这可能是一个不好的做法:http: //java.sun.com/docs/books/tutorial/essential/exceptions/runtime.html

任何 RuntimeException 或未经检查的异常都可以帮助您。也许您需要创建自己的 RuntimeException

于 2009-09-02T18:05:46.613 回答
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() 方法结束时,线程将停止。

于 2009-09-02T18:10:06.073 回答
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); } 
    }
}
于 2021-04-01T05:07:10.913 回答
-1

将异常包装在 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;
    }
}
于 2016-04-18T07:33:34.630 回答