我有一个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变量,它在幕后做了同样的事情。

其他回答

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

如果你实现Thread。在启动线程的类UncaughtExceptionHandler中,你可以设置并重新抛出异常:

public final class ThreadStarter implements Thread.UncaughtExceptionHandler{

private volatile Throwable initException;

    public void doSomeInit(){
        Thread t = new Thread(){
            @Override
            public void run() {
              throw new RuntimeException("UNCAUGHT");
            }
        };
        t.setUncaughtExceptionHandler(this);

        t.start();
        t.join();

        if (initException != null){
            throw new RuntimeException(initException);
        }

    }

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        initException =  e;
    }    

}

这将导致以下输出:

Exception in thread "main" java.lang.RuntimeException: java.lang.RuntimeException: UNCAUGHT
    at com.gs.gss.ccsp.enrichments.ThreadStarter.doSomeInit(ThreadStarter.java:24)
    at com.gs.gss.ccsp.enrichments.ThreadStarter.main(ThreadStarter.java:38)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: java.lang.RuntimeException: UNCAUGHT
    at com.gs.gss.ccsp.enrichments.ThreadStarter$1.run(ThreadStarter.java:15)

对于那些需要停止所有线程运行,并在其中任何一个线程在异常上停止时重新运行所有线程的人:

@Override
public void onApplicationEvent(ContextRefreshedEvent event) {

     // could be any function
     getStockHistory();

}


public void getStockHistory() {

     // fill a list of symbol to be scrapped
     List<String> symbolListNYSE = stockEntityRepository
     .findByExchangeShortNameOnlySymbol(ContextRefreshExecutor.NYSE);


    storeSymbolList(symbolListNYSE, ContextRefreshExecutor.NYSE);

}


private void storeSymbolList(List<String> symbolList, String exchange) {

    int total = symbolList.size();

    // I create a list of Thread 
    List<Thread> listThread = new ArrayList<Thread>();

    // For each 1000 element of my scrapping ticker list I create a new Thread
    for (int i = 0; i <= total; i += 1000) {
        int l = i;

        Thread t1 = new Thread() {

            public void run() {

                // just a service that store in DB my ticker list
                storingService.getAndStoreStockPrice(symbolList, l, 1000, 
                MULTIPLE_STOCK_FILL, exchange);

            }

        };

    Thread.UncaughtExceptionHandler h = new Thread.UncaughtExceptionHandler() {
            public void uncaughtException(Thread thread, Throwable exception) {

                // stop thread if still running
                thread.interrupt();

                // go over every thread running and stop every one of them
                listThread.stream().forEach(tread -> tread.interrupt());

                // relaunch all the Thread via the main function
                getStockHistory();
            }
        };

        t1.start();
        t1.setUncaughtExceptionHandler(h);

        listThread.add(t1);

    }

}

总结如下:

你有一个主函数来创建多个线程,每个线程都有UncaughtExceptionHandler,由线程内的任何异常触发。将每个线程添加到一个列表中。如果一个UncaughtExceptionHandler被触发,它将循环遍历列表,停止每个线程并重新启动主函数重新创建所有线程。

这解释了线程的状态转换取决于是否发生异常:

来源:http://www-public.imtbs-tsp.eu/~gibson/Teaching/CSC7322/L8-ExceptionsAndThreads.pdf

同样在Java 8中,你可以把Dan Cruz的答案写成:

Thread t = new Thread(()->{
            System.out.println("Sleeping ...");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                System.out.println("Interrupted.");
            }
            System.out.println("Throwing exception ...");
            throw new RuntimeException(); });


t.setUncaughtExceptionHandler((th, ex)-> log(String.format("Exception in thread %d id: %s", th.getId(), ex)));
t.start();