Перезапуск потоков с использованием UncaughtExceptionHandler

1. UncaughtExceptionHandler

Приложения Java имеют два типа исключений – проверяемые исключения и непроверяемые исключения. Проверяемые исключения должны быть указаны в предложении throws метода или перехвачены внутри них. Непроверяемые исключения не обязательно указывать или перехватывать.

Когда проверяемое исключение выбрасывается внутри метода run() объекта Thread, мы должны перехватить и обработать его соответствующим образом, поскольку метод run() не принимает условие throws. Но когда непроверяемое исключение выбрасывается внутри метода run() объекта Thread, поведение по умолчанию — записать трассировку стека в консоль(или записать ее в файл журнала ошибок) и выйти из программы.

К счастью, Java предоставляет нам механизм для перехвата и обработки непроверенных исключений, выдаваемых в экземпляре Thread, чтобы избежать сбоя программы. Это можно сделать с помощью UncaughtExceptionHandler.

2. Пример UncaughtExceptionHandler

В этом примере мы создали поток, который пытается разобрать несколько строк, которые должны быть целыми числами. Мы написали метод run() таким образом, что он выдает «java.lang.NumberFormatException» во время своего выполнения.

Поскольку программа не пытается поймать это исключение, исключение проходит через уровень JVM, и поток уничтожается. Это абсолютно нормальное поведение, но МОЖЕТ НЕ быть желаемым поведением.

2.1 Без UncaughtExceptionHandler

В реальном приложении вы хотели бы попытаться выполнить критическую задачу несколько раз, даже если она не удалась пару раз. Наш пример ниже демонстрирует вариант использования, сначала без использования UncaughtExceptionHandler; что приводит к немедленному завершению потока после сбоя.

class Task implements Runnable{@Overridepublic void run(){System.out.println(Integer.parseInt("123"));System.out.println(Integer.parseInt("234"));System.out.println(Integer.parseInt("345"));System.out.println(Integer.parseInt("XYZ")); //This will cause NumberFormatExceptionSystem.out.println(Integer.parseInt("456"));}}
public class DemoThreadExample{public static void main(String[] args){Task task = new Task();Thread thread = new Thread(task);thread.start();}}

Ниже приведен вывод, который мы получаем при запуске потока:

123234345Exception in thread "Thread-0" java.lang.NumberFormatException: For input string: "XYZ"at java.lang.NumberFormatException.forInputString(Unknown Source)at java.lang.Integer.parseInt(Unknown Source)at java.lang.Integer.parseInt(Unknown Source)at examples.algorithms.sleepingbarber.Task.run(DemoThreadExample.java:24)at java.lang.Thread.run(Unknown Source)

2.2 С UncaughtExceptionHandler

Давайте добавим одну реализацию UncaughtExceptionHandler для перехвата любого непроверенного исключения во время выполнения.

class ExceptionHandler implements UncaughtExceptionHandler{public void uncaughtException(Thread t, Throwable e){System.out.printf("An exception has been captured\n");System.out.printf("Thread: %s\n", t.getId());System.out.printf("Exception: %s: %s\n", e.getClass().getName(), e.getMessage());System.out.printf("Stack Trace: \n");e.printStackTrace(System.out);System.out.printf("Thread status: %s\n", t.getState());new Thread(new Task()).start();}}

Теперь добавьте этот обработчик исключений в поток.

class Task implements Runnable{@Overridepublic void run(){Thread.currentThread().setUncaughtExceptionHandler(new ExceptionHandler());System.out.println(Integer.parseInt("123"));System.out.println(Integer.parseInt("234"));System.out.println(Integer.parseInt("345"));System.out.println(Integer.parseInt("XYZ")); //This will cause NumberFormatExceptionSystem.out.println(Integer.parseInt("456"));}}

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

123234345An exception has been capturedThread: 1394Exception: java.lang.NumberFormatException: For input string: "XYZ"Stack Trace:java.lang.NumberFormatException: For input string: "XYZ"at java.lang.NumberFormatException.forInputString(Unknown Source)at java.lang.Integer.parseInt(Unknown Source)at java.lang.Integer.parseInt(Unknown Source)at examples.algorithms.sleepingbarber.Task.run(DemoThreadExample.java:24)at java.lang.Thread.run(Unknown Source)Thread status: RUNNABLE123234345An exception has been capturedThread: 1395Exception: java.lang.NumberFormatException: For input string: "XYZ"Stack Trace:java.lang.NumberFormatException: For input string: "XYZ"at java.lang.NumberFormatException.forInputString(Unknown Source)at java.lang.Integer.parseInt(Unknown Source)at java.lang.Integer.parseInt(Unknown Source)at examples.algorithms.sleepingbarber.Task.run(DemoThreadExample.java:24)at java.lang.Thread.run(Unknown Source)Thread status: RUNNABLE123234345

3. Заключение

UncaughtExceptionHandler помогает вам запустить поток таким образом, что он будет работать до тех пор, пока его задача не будет выполнена. Этого можно достичь и с помощью других концепций многопоточности.

Обратите внимание, что UncaughtExceptionHandler можно использовать только для повышения надежности ведения журнала без перезапуска потока, поскольку часто журналы по умолчанию не предоставляют достаточно информации о контексте при сбое выполнения потока.

Прокрутить вверх