Как поймать исключение из потока - 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 ]

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

Используйте 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();
37 голосов
/ 01 июля 2011

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

Threads and Exception Handling

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

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

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

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

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

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

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

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

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

Пожалуйста, посмотрите на Thread.UncaughtExceptionHandler

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

3 голосов
/ 19 апреля 2016

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

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

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

Вы можете настроить 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>);
3 голосов
/ 01 июля 2011

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

2 голосов
/ 23 мая 2017

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

1 голос
/ 01 июля 2019

Также из 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();
1 голос
/ 01 июля 2011

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

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

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