如何从java线程中抛出一个检查过的异常?

时间:2009-09-02 17:54:51

标签: java exception thread-exceptions

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

run() in (...).Listener cannot implement run() in java.lang.Runnable; overridden method does not throw java.io.IOException

我希望异常终止该线程,并让它在父线程中的某处被捕获。这是可能实现还是我必须处理内部线程中的每个异常?

10 个答案:

答案 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;
    }
}