Как поймать Исключение из потока

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

    }
  }
}

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

Ответ 1

Используйте 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

Это объясняет переход состояния потоков, зависящий от того, произошло ли исключение:

Threads and Exception Handling

Ответ 3

Это потому, что исключения являются локальными для потока, а ваш основной поток фактически не видит метод run. Я предлагаю вам прочитать больше о том, как работает многопоточность, но чтобы быстро подвести итог: ваш призыв к start запускает другой поток, совершенно не связанный с вашим основным потоком. Звонок join просто ждет, когда это будет сделано. Исключение, которое выбрасывается в поток и никогда не перехватывается, завершает его, поэтому join возвращается в ваш основной поток, но само исключение теряется.

Если вы хотите знать об этих необработанных исключениях, вы можете попробовать это:

Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println("Caught " + e);
    }
});

Более подробную информацию об обработке необработанных исключений можно найти здесь.

Ответ 4

Скорее всего;

  • вам не нужно передавать исключение из одного потока в другой.
  • если вы хотите обработать исключение, просто сделайте это в потоке, который его бросил.
  • Ваш основной поток не должен ждать из фонового потока в этом примере, что на самом деле означает, что вам вообще не нужен фоновый поток.

Однако давайте предположим, что вам нужно обрабатывать исключение из дочернего потока другой. Я бы использовал 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

Пожалуйста, взгляните на Thread.UncaughtExceptionHandler

Лучше (альтернативным) способом является использование Callable и Future, чтобы получить тот же результат...

Ответ 6

Используйте Callable вместо Thread, затем вы можете вызвать Future#get(), который выдает любое исключение, которое вызывало Callable.

Ответ 7

В настоящее время вы ловите только RuntimeException, подкласс класса Exception. Но ваше приложение может бросить другие подклассы Exception. Поймайте общий Exception в дополнение к RuntimeException

Так как многие вещи были изменены на Threading front, используйте расширенный API java.

Предпочитайте заранее java.util.concurrent API для многопоточности, например ExecutorService или ThreadPoolExecutor.

Вы можете настроить ThreadPoolExecutor для обработки исключений.

Пример страницы документации оракула:

Переопределить

protected void afterExecute(Runnable r,
                            Throwable t)

Метод, вызванный по завершении выполнения данного Runnable. Этот метод вызывается потоком, выполняющим задачу. Если непустой, 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>);

Ответ 8

У меня возникла такая же проблема... небольшая работа (только для реализации не анонимных объектов)... мы можем объявить объект исключения уровня класса как 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;
            }
        }
}     

вызов checkForException() после join()

Ответ 9

Вы играли с setDefaultUncaughtExceptionHandler() и похожими методами класса Thread? Из API: "Установив обработчик исключаемых исключений по умолчанию, приложение может изменить способ обработки неперехваченных исключений (например, ведение журнала на конкретном устройстве или файле) для тех потоков, которые уже принимают любое поведение по умолчанию системы".

Вы можете найти ответ на свою проблему... удачи!: -)

Ответ 10

Также из 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();

Ответ 11

Можно ли задать основной поток как uncaughtExceptionHandler для вашего второго потока.

Thread API

Ответ 12

Почти всегда неправильно распространять 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.

Ответ 13

Если вы реализуете Thread.UncaughtExceptionHandler в классе, который запускает Threads, вы можете установить и затем перестроить исключение:

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)

Ответ 14

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

если мы не устанавливаем uncaughtExceptionHandler, тогда ThreadGroup Threads действует как обработчик.

 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

Ответ 15

Мое решение с 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.");
}

Ответ 16

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