首页 文章

如何从线程中捕获异常

提问于
浏览
129

我有Java主类,在类中,我开始一个新的线程,在主要的,它等待线程死亡 . 在某些时刻,我从线程中抛出一个运行时异常,但我无法捕获从主类中的线程抛出的异常 .

这是代码:

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)
    {

    }
  }
}

谁知道为什么?

14 回答

  • 0

    请看看Thread.UncaughtExceptionHandler

    更好(替代)的方法是使用CallableFuture来获得相同的结果......

  • 35

    我使用RxJava的解决方案:

    @Test(expectedExceptions = TestException.class)
    public void testGetNonexistentEntry() throws Exception
    {
        // using this to work around the limitation where the errors in onError (in subscribe method)
        // cannot be thrown out to the main thread
        AtomicReference<Exception> ex = new AtomicReference<>();
        URI id = getRandomUri();
        canonicalMedia.setId(id);
    
        client.get(id.toString())
            .subscribe(
                m ->
                    fail("Should not be successful"),
                e ->
                    ex.set(new TestException()));
    
        for(int i = 0; i < 5; ++i)
        {
            if(ex.get() != null)
                throw ex.get();
            else
                Thread.sleep(1000);
        }
        Assert.fail("Cannot find the exception to throw.");
    }
    
  • 2

    如果在启动Threads的类中实现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)
    
  • 0

    使用 Thread.UncaughtExceptionHandler .

    Thread.UncaughtExceptionHandler h = new Thread.UncaughtExceptionHandler() {
        public void uncaughtException(Thread th, Throwable ex) {
            System.out.println("Uncaught exception: " + ex);
        }
    };
    Thread t = new Thread() {
        public void run() {
            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(h);
    t.start();
    
  • 16

    目前你只捕捉 RuntimeException ,一个 Exception 的子类 . 但是你的应用程序可能抛出Exception的其他子类 . 除了 RuntimeException 之外,还要 grab 通用 Exception

    由于线程前端已经更改了许多内容,因此请使用高级java API .

    首选java.util.concurrent API用于多线程,如 ExecutorServiceThreadPoolExecutor .

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

    来自oracle文档页面的示例:

    覆盖

    protected void afterExecute(Runnable r,
                                Throwable t)
    

    完成给定Runnable的执行后调用的方法 . 执行任务的线程调用此方法 . 如果为非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>);
    
  • 0

    你不能这样做,因为它没有't really make sense. If you hadn'调用 t.join() 然后当 t 线程抛出异常时你的主线程可能在代码中的任何地方 .

  • 1

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

    Threads and Exception Handling

  • 5

    那's because exceptions are local to a thread, and your main thread doesn' t实际上看到了 run 方法 . 我建议你阅读更多关于线程如何工作的内容,但要快速总结一下:你对 start 的调用启动了一个不同的线程,完全与你的主线程无关 . 对 join 的调用只是等待它完成 . 在线程中抛出并且从未捕获的异常终止它,这就是 join 在主线程上返回的原因,但异常本身也会丢失 .

    如果您想了解这些未捕获的异常,可以尝试这样做:

    Thread.setDefaultExceptionHandler(new UncaughtExceptionHandler() {
        public void unchaughtException(Thread t, Throwable e) {
            System.out.println("Caught " + e);
        }
    });
    

    有关未捕获的异常处理的更多信息,请参见here .

  • 2

    使用 Callable 而不是Thread,然后你可以调用 Future#get() ,它会抛出Callable投掷的任何异常 .

  • 0

    Thread中的异常处理:默认情况下,run()方法不会抛出任何异常,因此必须在那里捕获并处理run方法中的所有已检查异常,对于运行时异常,我们可以使用UncaughtExceptionHandler . UncaughtExceptionHandler是Java提供的用于处理Thread run方法中的异常的接口 . 因此,我们可以使用setUncaughtExceptionHandler()方法实现此接口并将实现类重新设置回Thread对象 . 但是这个处理程序必须在我们在脚踏上调用start()之前设置 .

    如果我们不设置uncaughtExceptionHandler,则Threads ThreadGroup充当处理程序 .

    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给出了很好的解释

  • 33

    最有可能的;

    • 您不需要将异常从一个线程传递到另一个线程 .

    • 如果你想处理一个异常,只需在抛出它的线程中进行 .

    • 你的主线程根本不需要后台线程 .

    但是,假设您确实需要处理子线程中的异常 . 我会像这样使用ExecutorService:

    ExecutorService executor = Executors.newSingleThreadExecutor();
    Future<Void> future = executor.submit(new Callable<Void>() {
        @Override
        public Void call() throws Exception {
            System.out.println("** Started");
            Thread.sleep(2000);
            throw new IllegalStateException("exception from thread");
        }
    });
    try {
        future.get(); // raises ExecutionException for any uncaught exception in child
    } catch (ExecutionException e) {
        System.out.println("** RuntimeException from thread ");
        e.getCause().printStackTrace(System.out);
    }
    executor.shutdown();
    System.out.println("** Main stopped");
    

    版画

    ** Started
    ** RuntimeException from thread 
    java.lang.IllegalStateException: exception from thread
        at Main$1.call(Main.java:11)
        at Main$1.call(Main.java:6)
        at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
        at java.util.concurrent.FutureTask.run(FutureTask.java:138)
        at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
        at java.lang.Thread.run(Thread.java:662)
    ** Main stopped
    
  • 1

    您是否使用过setDefaultUncaughtExceptionHandler()和Thread类的相似方法?从API:“通过设置默认的未捕获异常处理程序,应用程序可以更改已经接受任何”默认“行为的线程处理未捕获异常的方式(例如,记录到特定设备或文件)系统提供 . “

    你可能会找到你问题的答案......祝你好运! :-)

  • 178

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

    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()

  • -4

    扩展 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 .

相关问题