Как поймать исключение из потока - PullRequest
140 голосов
/ 01 июля 2011

У меня есть основной класс 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)
    {

    }
  }
}

Кто-нибудь знает почему?

Ответы [ 15 ]

0 голосов
/ 20 декабря 2016

Мое решение с 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.");
}
0 голосов
/ 26 августа 2015

Обработка исключений в потоке: по умолчанию метод run () не генерирует никаких исключений, поэтому все проверенные исключения внутри метода run должны быть перехвачены и обработаны только там, а для исключений времени выполнения мы можем использовать UncaughtExceptionHandler. UncaughtExceptionHandler - это интерфейс, предоставляемый Java для обработки исключений в методе выполнения потока. Таким образом, мы можем реализовать этот интерфейс и вернуть наш реализующий класс обратно в объект Thread, используя метод setUncaughtExceptionHandler (). Но этот обработчик должен быть установлен до того, как мы вызовем 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

0 голосов
/ 16 июля 2013

Если вы реализуете 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 голосов
/ 01 июля 2011

Расширение Thread почти всегда неверно. Я не могу утверждать это достаточно сильно.

Правило многопоточности # 1: расширение Thread неверно. *

Если вместо этого вы реализуете 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.

0 голосов
/ 01 июля 2011

Вы не можете сделать это, так как это не имеет смысла. Если вы не вызвали t.join(), тогда ваш основной поток может быть где угодно в коде, когда поток t выдает исключение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...