代码之家  ›  专栏  ›  技术社区  ›  Dinesh

如何将异常传播到Java中的调用线程[复制]

  •  0
  • Dinesh  · 技术社区  · 7 年前

    我有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)
        {
    
        }
      }
    }
    

    有人知道为什么吗?

    0 回复  |  直到 8 年前
        1
  •  186
  •   Go Dan    15 年前

    使用A 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();
    
        2
  •  36
  •   abyx    15 年前

    这是因为异常是线程的局部异常,而您的主线程实际上没有看到 run 方法。我建议您阅读更多关于线程如何工作的内容,但要快速总结一下:您的调用 start 启动一个不同的线程,完全与主线程无关。呼唤 join 只需等待完成。在线程中抛出但从未捕获的异常终止了线程,这就是为什么 参加 返回主线程,但异常本身丢失。

    如果要了解这些未捕获的异常,可以尝试以下操作:

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

    可以找到有关未捕获异常处理的更多信息 here .

        3
  •  36
  •   Kalle Richter    8 年前

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

    是否发生离子:

    Threads and Exception Handling

        4
  •  17
  •   rogerdpack    10 年前

    极有可能;

    • 您不需要将异常从一个线程传递到另一个线程。
    • 如果您想处理一个异常,只需在抛出它的线程中进行处理。
    • 在本例中,您的主线程不需要从后台线程等待,这实际上意味着您根本不需要后台线程。

    但是,假设您确实需要处理来自另一个子线程的异常。我会使用这样的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
    
        5
  •  5
  •   Ravindra babu    10 年前

    请看一下 Thread.UncaughtExceptionHandler

    更好的(替代)方法是使用 Callable Future 为了得到同样的结果…

        6
  •  3
  •   artbristol    15 年前

    使用 Callable 而不是线程,那么您可以调用 Future#get() 它会抛出调用程序抛出的任何异常。

        7
  •  2
  •   Ravindra babu    10 年前

    目前你只抓到 RuntimeException ,的子类 Exception . 但是您的应用程序可能会抛出 Exception . 捕获泛型 例外 除了 运行期异常

    由于线程前发生了很多变化,所以使用高级Java API。

    首选高级java.util.concurrent API 用于多线程 ExecutorService ThreadPoolExecutor .

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

    Oracle文档页面中的示例:

    重写

    protected void afterExecute(Runnable r,
                                Throwable t)
    

    在执行给定的可运行文件完成时调用的方法。此方法由执行任务的线程调用。如果非空,则throwable是导致执行突然终止的未捕获的RuntimeException或错误。

    示例代码:

    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>);
    
        8
  •  2
  •   Java Beginner    9 年前

    我也面临同样的问题…几乎没有解决方案(仅用于实现而不是匿名对象)…我们可以将类级异常对象声明为空…然后在catch块内对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()。

        9
  •  1
  •   Dr. Snuggles    15 年前

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

    你可能会在那里找到问题的答案…祝你好运!-)

        10
  •  0
  •   Qwerky    15 年前

    扩展几乎总是错误的 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。

        11
  •  0
  •   Stefano    13 年前

    如果在启动线程的类中实现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)
    
        12
  •  0
  •   Jatinder Pal    10 年前

    线程中的异常处理:默认情况下,run()方法不会引发任何异常,因此必须捕获run方法中检查的所有异常,并仅在其中进行处理,对于运行时异常,我们可以使用uncaughtexceptionhandler。UncaughtExceptionHandler是Java提供的接口,用于处理线程运行方法中的异常。因此,我们可以实现这个接口,并使用setUncaughtExceptionHandler()方法将实现类设置回线程对象。但在调用tread上的start()之前必须设置这个处理程序。

    如果我们不设置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

        13
  •  0
  •   Ascendant    9 年前

    我的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.");
    }
    
        14
  •  -5
  •   Mathias Schwarz    15 年前

    你不能这样做,因为这不太合理。如果你没有打电话 t.join() 然后,当 t 线程引发异常。