使用UncaughtExceptionHandler重启线程
先复习Java中的异常
java.lang.Throwable 顶层父类
|– Error错误:JVM内部的严重问题,如OOM,程序员无法在代码中无法处理。
|–Exception异常:普通的问题。通过合理的处理,程序还可以回到正常执行流程。要求程序员要进行处理。
|–RuntimeException:未检查异常(unchecked exception)。 这类异常是程序员的逻辑问题,由于程序员的疏忽导致的错误(如数组越界,空指针等)。
Java编译器不进行强制要求处理。 也就是说,这类异常在程序中,可以进行处理,也可以不处理。
|–非RuntimeException:已检查异常(checked exception)、编译时异常。这类异常是由一些外部的偶然因素所引起的。Java编译器强制要求处理。也就是说,
程序必须进行对这类异常进行处理,throw,throws或者try catch。
而线程Thread的的run()方法不接受抛出语句,因此对于已检查异常,我们必须进行捕获并处理,如:
@Override
public void run() {
FileInputStream fis = null;
try {
fis = new FileInputStream(new File(""));
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
} }
}
而对于未检查异常,如果在run()方法中运行出现了未检查异常,那么默认的行为是将堆栈跟踪信息写到控制台中(或者记录到错误日志文件中),然后退出程序。
Java为我们提供了一个机制,用来捕获并处理在一个线程对象中抛出的未检查异常,以避免程序终止。用UncaughtExceptionHandler来实现这种机制。
不使用UncaughtExceptionHandler的场景:
public class MyThread implements Runnable { @Override
public void run() {
Integer.parseInt("yangyongjie");
System.out.println("expect");
} }
控制台输出:
Exception in thread "Thread-0" java.lang.NumberFormatException: For input string: "yangyongjie"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:492)
at java.lang.Integer.parseInt(Integer.java:527)
at com.yang.spbo.other.thread.MyThread.run(MyThread.java:15)
at java.lang.Thread.run(Thread.java:745)
此时,线程立即终止,并没有执行之后的代码输出字符串“expect”。若在执行很重要的代码之前,出现了未检查异常,导致了线程终止,那么就会导致业务异常。
使用UncaughtExceptionHandler后:
首先,自定义异常处理器实现UncaughtExceptionHandler接口,用来捕获和处理运行时出现的未检查异常(RuntimeException)
public class CustomUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { private static final Logger LOGGER = LoggerFactory.getLogger(CustomUncaughtExceptionHandler.class); /**
* 可以自定义处理方案,如不断重试、将异常任务存入数据库等
*
* @param t
* @param e
*/
@Override
public void uncaughtException(Thread t, Throwable e) {
LOGGER.error("An exception has been captured,Thread: {}", t.getId());
LOGGER.error("Exception: {}: {}", e.getClass().getName(), e.getMessage());
LOGGER.error("Thread status: {}", t.getState());
new Thread(new MyThread()).start();
}
}
接着,将异常处理器添加到线程中
public class MyThread implements Runnable { @Override
public void run() {
// 异常处理器
Thread.currentThread().setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
Integer.parseInt("yangyongjie");
System.out.println("expect");
}
}
再次运行,程序能够持续执行run方法。实际上,如果线程完成了任务,那么它在退出时不会抛出任何异常,从而完成自身生命周期
An exception has been captured
Thread: 10
Exception: java.lang.NumberFormatException: For input string: "yangyongjie"
Stack Trace:
java.lang.NumberFormatException: For input string: "yangyongjie"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:492)
at java.lang.Integer.parseInt(Integer.java:527)
at com.yang.spbo.other.thread.MyThread.run(MyThread.java:22)
at java.lang.Thread.run(Thread.java:745)
Thread status: RUNNABLE
An exception has been captured
Thread: 12
Exception: java.lang.NumberFormatException: For input string: "yangyongjie"
Stack Trace:
java.lang.NumberFormatException: For input string: "yangyongjie"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:492)
at java.lang.Integer.parseInt(Integer.java:527)
at com.yang.spbo.other.thread.MyThread.run(MyThread.java:22)
at java.lang.Thread.run(Thread.java:745)
Thread status: RUNNABLE
请注意:UncaughtExceptionHandler可以在无需重启线程的条件下,将日志记录变得更加健壮,因为默认日志在线程执行失败时,不会提供足够的上下文信息。
线程池异常处理,自定义创建线程的工厂,在创建线程时指定
/**
* 创建线程的工厂,指定有意义的线程组名称,方便回溯
*
* @author yangyongjie
* @date 2019/8/14
* @desc
*/
public class CustomThreadFactory implements ThreadFactory {
/**
* 线程池中的线程名称前缀
*/
private String namePrefix; /**
* 用于线程的名称递增排序
*/
private AtomicInteger atomicInteger = new AtomicInteger(1); public CustomThreadFactory(String whatFeaturOfGroup) {
this.namePrefix = "From CustomThreadFactory-" + whatFeaturOfGroup + "-worker-";
} @Override
public Thread newThread(Runnable r) {
String name = namePrefix + atomicInteger.getAndIncrement();
Thread thread = new Thread(r, name);
thread.setUncaughtExceptionHandler(new CustomUncaughtExceptionHandler());
return thread;
}
} /**
* 用来捕获和处理运行时出现的未检查异常(RuntimeException)
* 并重启线程
*
* @author yangyongjie
* @date 2019/11/26
* @desc
*/
public class CustomUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { private static final Logger LOGGER = LoggerFactory.getLogger(CustomUncaughtExceptionHandler.class); /**
* 可以自定义处理方案,如不断重试、将异常任务存入数据库等
*
* @param t
* @param e
*/
@Override
public void uncaughtException(Thread t, Throwable e) {
LOGGER.error("uncaughtException:" + e.getMessage(), e);
MDCUtil.removeWithOutContext();
}
}
最新文章
- 转职成为TypeScript程序员的参考手册
- 一个简单的js实现倒计时函数
- 【数学】Jersey Politics
- [原创] 【2014.12.02更新网盘链接】基于EasySysprep4.1的 Windows 7 x86/x64 『视频』封装
- 解决iOS9下隐藏App返回按钮文字导致的诡异闪屏问题
- 这世上倒底有没有神仙——说“Excel不是数据库,是不是犯了白马非马论的错误??
- JLINK V8 升级5.12E 在MDK5.20不变砖
- Swift学习资源
- XPath具体解释
- 【转载】locate命令的使用
- Java---XML的解析(1)-DOM解析
- IIS7 MVC网站生成、发布
- QReadWriteLock上锁容忍的等待时间是多久?
- 15个最佳jQuery的翻页书效果的例子
- HelloHibernate的创建过程
- C++系列总结——new和delete
- webpack学习笔记-2-file-loader 和 url-loader
- CentOS下下载软件,不安装的方法
- Halcon小函数的封装和代码导出
- 怎么能让json_decode解析带斜杠的字符串