如何使用UncaughtExceptionHandler重新启动线程
如何使用UncaughtExceptionHandler
重新启动线程
我们已经知道 Java 中有两种异常。 受检的异常和非受检的异常。 必须在方法的throws
子句中指定受检的异常或将其捕获在其中。 无需指定或捕获非受检的异常。 当在Thread
对象的run()
方法内引发受检异常时,由于run()
方法不接受throws
子句,因此我们必须相应地对其进行处理。 但是,当在Thread
对象的run()
方法内引发非受检的异常时,默认行为是在控制台中写入栈跟踪(或将其记录在错误日志文件中)并退出程序。
幸运的是,Java 为我们提供了一种机制来捕获和处理Thread
对象中抛出的非受检的异常,从而避免程序结束。 这可以使用UncaughtExceptionHandler
完成。
让我们以UncaughtExceptionHandler
用法为例。 在此示例中,我们创建了一个线程,该线程尝试解析一些应该为整数的字符串。 我们编写了run()
方法,使其在执行过程中抛出“ java.lang.NumberFormatException
”。 由于程序不会尝试捕获此异常,因此异常会在 JVM 级别浮动,并且线程被杀死。 这绝对是正常行为,但可能不是您期望的行为。
不使用UncaughtExceptionHandler
在现实生活中的应用程序中,即使几次失败,您也想尝试执行一次多次重要任务。 下面的示例演示了用例,首先不使用UncaughtExceptionHandler
; 导致线程在失败后立即死亡。
Task.java
class Task implements Runnable
{
@Override
public 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 NumberFormatException
System.out.println(Integer.parseInt("456"));
}
}
DemoThreadExample.java
public class DemoThreadExample
{
public static void main(String[] args)
{
Task task = new Task();
Thread thread = new Thread(task);
thread.start();
}
}
下面是运行线程时得到的输出:
123
234
345
Exception 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)
使用UncaughtExceptionHandler
之后
让我们添加一个UncaughtExceptionHandler
实现,以在运行时捕获任何非受检的异常。
ExceptionHandler.java
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
{
@Override
public 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 NumberFormatException
System.out.println(Integer.parseInt("456"));
}
}
现在再次运行上面的示例。 这将连续运行。 在现实生活中,如果该任务能够完成任务,那么它将在不引发任何异常的情况下退出并完成其生命周期。
123
234
345
An exception has been captured
Thread: 1394
Exception: 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: RUNNABLE
123
234
345
An exception has been captured
Thread: 1395
Exception: 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: RUNNABLE
123
234
345
上面的实现可帮助您以某种方式运行线程,直到执行任务为止。 这也可以通过其他多线程概念来实现。
请注意,UncaughtExceptionHandler
也可以用于使日志记录更加健壮,而无需重新启动线程,因为在线程执行失败时,默认日志通常无法提供足够的上下文信息。