我有一个Java main类,在这个类中,我启动一个新线程,在main中,它等待线程死亡。在某个时刻,我从线程抛出运行时异常,但我无法捕获从主类中的线程抛出的异常。

代码如下:

public class Test extends Thread
{
  public static void main(String[] args) throws InterruptedException
  {
    Test t = new Test();

    try
    {
      t.start();
      t.join();
    }
    catch(RuntimeException e)
    {
      System.out.println("** RuntimeException from main");
    }

    System.out.println("Main stoped");
  }

  @Override
  public void run()
  {
    try
    {
      while(true)
      {
        System.out.println("** Started");

        sleep(2000);

        throw new RuntimeException("exception from thread");
      }
    }
    catch (RuntimeException e)
    {
      System.out.println("** RuntimeException from thread");

      throw e;
    } 
    catch (InterruptedException e)
    {

    }
  }
}

有人知道为什么吗?


当前回答

AtomicReference也是一个将错误传递给主线程的解决方案。与Dan Cruz的方法相同。

AtomicReference<Throwable> errorReference = new AtomicReference<>();

    Thread thread = new Thread() {
        public void run() {
            throw new RuntimeException("TEST EXCEPTION");

        }
    };
    thread.setUncaughtExceptionHandler((th, ex) -> {
        errorReference.set(ex);
    });
    thread.start();
    thread.join();
    Throwable newThreadError= errorReference.get();
    if (newThreadError!= null) {
        throw newThreadError;
    }  

唯一的变化是你可以使用AtomicReference而不是创建一个volatile变量,它在幕后做了同样的事情。

其他回答

扩展Thread几乎总是错误的。我怎么强调都不为过。

多线程规则1:扩展线程是错误的

如果您实现Runnable,您将看到您预期的行为。

public class Test implements Runnable {

  public static void main(String[] args) {
    Test t = new Test();
    try {
      new Thread(t).start();
    } catch (RuntimeException e) {
      System.out.println("** RuntimeException from main");
    }

    System.out.println("Main stoped");

  }

  @Override
  public void run() {
    try {
      while (true) {
        System.out.println("** Started");

        Thread.sleep(2000);

        throw new RuntimeException("exception from thread");
      }
    } catch (RuntimeException e) {
      System.out.println("** RuntimeException from thread");
      throw e;
    } catch (InterruptedException e) {

    }
  }
}

产生;

Main stoped
** Started
** RuntimeException from threadException in thread "Thread-0" java.lang.RuntimeException: exception from thread
    at Test.run(Test.java:23)
    at java.lang.Thread.run(Thread.java:619)

除非你想改变你的应用程序使用线程的方式,在99.9%的情况下你不会这样做。如果你认为你属于0.1%的情况,请参阅规则1。

你不能这样做,因为这真的没有意义。如果您没有调用t.join(),那么当t线程抛出异常时,主线程可能位于代码中的任何位置。

目前您只捕获RuntimeException, Exception的一个子类。但是您的应用程序可能会抛出Exception的其他子类。捕获除RuntimeException之外的泛型异常

由于线程前端已经改变了许多东西,请使用高级java API。

优先选择高级java.util.concurrent API用于多线程,如ExecutorService或ThreadPoolExecutor。

您可以自定义ThreadPoolExecutor来处理异常。

示例来自oracle文档页:

覆盖

protected void afterExecute(Runnable r,
                            Throwable t)

方法,在完成给定可运行对象的执行时调用。此方法由执行任务的线程调用。如果非null,则Throwable是导致执行突然终止的未捕获的RuntimeException或Error。

示例代码:

class ExtendedExecutor extends ThreadPoolExecutor {
   // ...
   protected void afterExecute(Runnable r, Throwable t) {
     super.afterExecute(r, t);
     if (t == null && r instanceof Future<?>) {
       try {
         Object result = ((Future<?>) r).get();
       } catch (CancellationException ce) {
           t = ce;
       } catch (ExecutionException ee) {
           t = ee.getCause();
       } catch (InterruptedException ie) {
           Thread.currentThread().interrupt(); // ignore/reset
       }
     }
     if (t != null)
       System.out.println(t);
   }
 }

用法:

ExtendedExecutor service = new ExtendedExecutor();

我在上面的代码中添加了一个构造函数:

 public ExtendedExecutor() { 
       super(1,5,60,TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(100));
   }

您可以更改此构造函数以满足您对线程数量的要求。

ExtendedExecutor service = new ExtendedExecutor();
service.submit(<your Callable or Runnable implementation>);

Exception handling in Thread : By default run() method doesn’t throw any exception, so all checked exceptions inside the run method has to be caught and handled there only and for runtime exceptions we can use UncaughtExceptionHandler. UncaughtExceptionHandler is an interface provided by Java to handle exceptions in a Thread run method. So we can implement this interface and set back our implementing class back to Thread object using setUncaughtExceptionHandler() method. But this handler has to be set before we call start() on the tread.

如果我们不设置uncaughtExceptionHandler,那么线程线程组就会充当一个处理程序。

 public class FirstThread extends Thread {

int count = 0;

@Override
public void run() {
    while (true) {
        System.out.println("FirstThread doing something urgent, count : "
                + (count++));
        throw new RuntimeException();
    }

}

public static void main(String[] args) {
    FirstThread t1 = new FirstThread();
    t1.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
        public void uncaughtException(Thread t, Throwable e) {
            System.out.printf("Exception thrown by %s with id : %d",
                    t.getName(), t.getId());
            System.out.println("\n"+e.getClass());
        }
    });
    t1.start();
}
}

http://coder2design.com/thread-creation/#exceptions给出了很好的解释

我也遇到过同样的问题……很少的工作(只用于实现而不是匿名对象)…我们可以将类级异常对象声明为null…然后在catch块for run方法中初始化它…如果在run方法中有错误,这个变量不会为空。然后,我们可以对这个特定的变量进行空检查,如果它不是空,那么线程执行内部就有异常。

class TestClass implements Runnable{
    private Exception ex;

        @Override
        public void run() {
            try{
                //business code
               }catch(Exception e){
                   ex=e;
               }
          }

      public void checkForException() throws Exception {
            if (ex!= null) {
                throw ex;
            }
        }
}     

join()后调用checkForException()