Java多线程:捕获线程异常
你处理过多线程中的异常吗?如何捕获多线程中发生的异常?捕获子线程的异常与捕获当前线程的异常一样简单吗?
除了try catch。Java中还可以通过异常处理器
UncaughtExceptionHandler
来处理那些未捕获的异常。
# 在当前线程捕获当前线程发生的异常:
/*** @author futao* @date 2020/6/17*/
@Slf4j
public class ExceptionInCurThread {public static void main(String[] args) {try {throw new RuntimeException("在主线程抛出异常,在主线程捕获");} catch (RuntimeException e) {log.error("捕获到异常", e);}}
}
结果:
结论:在当前线程通过
try catch
可以捕获当前线程抛出的异常。
# 可以在当前通过try catch
的方式捕获其他线程抛出的异常吗?'
/*** @author 喜欢天文的pony站长* Created on 2020/6/16.*/
public class ExceptionInChildThread implements Runnable {@Overridepublic void run() {throw new RuntimeException("子线程发生了异常...");}/*** 模拟子线程发生异常** @throws InterruptedException*/private static void exceptionThread() throws InterruptedException {new Thread(new ExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);new Thread(new ExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);new Thread(new ExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);new Thread(new ExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);}/*** 在主线程尝试通过try catch捕获异常*/private static void catchInMain() {try {exceptionThread();} catch (Exception e) {//无法捕获发生在其他线程中的异常log.error("捕获到了异常?", e);}}public static void main(String[] args) throws InterruptedException {ExceptionInChildThread.catchInMain();}}
(错误的)预期:
在运行第一个线程的时候发生了异常,被catch捕获,打印
捕获到了异常?
和异常堆栈且后面的线程将不会运行。
实际运行结果:
并不符合预期。
没有被try catch捕获。
后续的线程没有因为第一个线程发生异常而跳过。
结论:
无法在一个线程中通过try catch捕获另外一个线程的异常。
# 解决方案
在每个线程内部run()方法内通过try catch捕获当前线程发生的异常。
缺点:每个线程都需要编写重复的try catch 代码
使用线程异常处理器
UncaughtExceptionHandler
给所有线程设置统一的异常处理器
给每个线程设置特定的异常处理器
给线程组设置异常处理器
给线程池设置异常处理器
因为线程池也是通过
new Thread()
的方式创建的线程,所以思想与上面两种方法一致。注意:
execute()
与submit()
方式对 异常处理的不同。
# 在线程内部run()
通过try catch捕获异常
/*** @author 喜欢天文的pony站长* Created on 2020/6/16.*/
@Slf4j
public class ExceptionInChildThread implements Runnable {@Overridepublic void run() {try {//do something else...throw new RuntimeException("子线程发生了异常...");} catch (Exception e) {log.error("在线程内部捕获异常", e);}}/*** 模拟子线程发生异常** @throws InterruptedException*/private static void exceptionThread() throws InterruptedException {new Thread(new ExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);new Thread(new ExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);new Thread(new ExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);new Thread(new ExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);}/*** 在主线程尝试通过try catch捕获异常*/private static void catchInMain() {try {exceptionThread();} catch (Exception e) {//无法捕获发生在其他线程中的异常log.error("捕获到了异常?", e);}}public static void main(String[] args) throws InterruptedException {ExceptionInChildThread.catchInMain();}
}
结果:
成功在子线程内部
run()
方法捕获到了异常
# 使用线程异常处理器UncaughtExceptionHandler
当一个线程由于未捕获异常而退出时,JVM会把这个事件报告给应用程序提供的UncaughtExceptionHandler异常处理器
自定义线程异常处理器
/*** 自定义线程未捕获异常处理器** @author futao* @date 2020/6/17*/
public class CustomThreadUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {private static final Logger LOGGER = LoggerFactory.getLogger(CustomThreadUncaughtExceptionHandler.class);@Overridepublic void uncaughtException(Thread t, Throwable e) {LOGGER.error("捕获到线程发生的异常,线程信息:[{}]", JSON.toJSONString(t), e);}
}
使用:
1. 全局:
Thread.setDefaultUncaughtExceptionHandler(new CustomThreadUncaughtExceptionHandler());
通过调用Thread的静态方法
setDefaultUncaughtExceptionHandler()
,设置Thread的静态属性defaultUncaughtExceptionHandler
.为我们自定义的异常处理器。源码:
测试:
/*** @author 喜欢天文的pony站长* Created on 2020/6/16.*/
@Slf4j
public class ExceptionInChildThread implements Runnable {@Overridepublic void run() {throw new RuntimeException("子线程发生了异常...");}/*** 模拟子线程发生异常** @throws InterruptedException*/private static void exceptionThread() throws InterruptedException {new Thread(new ExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);new Thread(new ExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);new Thread(new ExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);new Thread(new ExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);}public static void main(String[] args) throws InterruptedException {//设置全局的线程异常处理器Thread.setDefaultUncaughtExceptionHandler(new CustomThreadUncaughtExceptionHandler());exceptionThread();}
}
结果: 成功捕获
2. 为指定线程设置特定的异常处理器
细心的同学已经发现了,在上面Thread类的截图中,还有一个实例属性
private volatile UncaughtExceptionHandler uncaughtExceptionHandler;
。通过给这个属性赋值,可以实现为每个线程对象设置不同的异常处理器。测试使用
/*** @author 喜欢天文的pony站长* Created on 2020/6/16.*/
@Slf4j
public class ExceptionInChildThread implements Runnable {@Overridepublic void run() {throw new RuntimeException("子线程发生了异常...");}/*** 模拟子线程发生异常** @throws InterruptedException*/private static void exceptionThread() throws InterruptedException {Thread thread1 = new Thread(new ExceptionInChildThread());//为指定线程设置特定的异常处理器thread1.setUncaughtExceptionHandler(new CustomThreadUncaughtExceptionHandler());thread1.start();TimeUnit.MILLISECONDS.sleep(200L);new Thread(new ExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);new Thread(new ExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);new Thread(new ExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);}public static void main(String[] args) throws InterruptedException {exceptionThread();}
}
结果: 成功捕获线程1的异常信息
3. 线程组
/*** @author futao* @date 2020/6/20*/
@Slf4j
public class ExceptionInThreadGroup implements Runnable {@Overridepublic void run() {throw new RuntimeException("线程任务发生了异常");}public static void main(String[] args) throws InterruptedException {ThreadGroup threadGroup = new ThreadGroup("只知道抛出异常的线程组...") {@Overridepublic void uncaughtException(Thread t, Throwable e) {super.uncaughtException(t, e);log.error("线程组内捕获到线程[{},{}]异常", t.getId(), t.getName(), e);}};ExceptionInThreadGroup exceptionInThreadGroup = new ExceptionInThreadGroup();new Thread(threadGroup, exceptionInThreadGroup, "线程1").start();TimeUnit.MILLISECONDS.sleep(300L);//优先获取绑定在thread对象上的异常处理器Thread thread = new Thread(threadGroup, exceptionInThreadGroup, "线程2");thread.setUncaughtExceptionHandler(new CustomThreadUncaughtExceptionHandler());thread.start();TimeUnit.MILLISECONDS.sleep(300L);new Thread(threadGroup, exceptionInThreadGroup, "线程3").start();}
}
结果:
4. 线程池
/*** @author futao* @date 2020/6/17*/
public class CatchThreadPoolException {public static void main(String[] args) {ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,4,1L,TimeUnit.MINUTES,new LinkedBlockingDeque<>(1024),new ThreadFactory() {@Overridepublic Thread newThread(Runnable r) {Thread thread = new Thread(r);//设置线程异常处理器thread.setUncaughtExceptionHandler(new CustomThreadUncaughtExceptionHandler());return thread;}});threadPoolExecutor.execute(new Runnable() {@Overridepublic void run() {throw new RuntimeException("execute()发生异常");}});threadPoolExecutor.submit(new Runnable() {@Overridepublic void run() {throw new RuntimeException("submit.run()发生异常");}});threadPoolExecutor.submit(new Callable<String>() {@Overridepublic String call() throws Exception {throw new RuntimeException("submit.call()发生异常");}});threadPoolExecutor.shutdown();}
}
结果: 并不符合预期,预期应该捕获三个异常
只捕获到了通过execute()提交的任务的异常
没有捕获到通过submit()提交的任务的异常
通过
afterExecute()
捕获submit()
任务的异常通过
submit()
方法的源码可以发现,submit()
是将runnable()
封装成了RunnableFuture<Void>
,并最终调用execute(ftask);
执行。
/*** @author futao* @date 2020/6/17*/
@Slf4j
public class CatchThreadPoolException {public static void main(String[] args) throws InterruptedException, ExecutionException {ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,4,1L,TimeUnit.MINUTES,new LinkedBlockingDeque<>(1024),new ThreadFactory() {@Overridepublic Thread newThread(Runnable r) {Thread thread = new Thread(r);//设置线程异常处理器thread.setUncaughtExceptionHandler(new CustomThreadUncaughtExceptionHandler());return thread;}}) {/*** 捕获{@code FutureTask<?>}抛出的异常** @param r* @param t*/@Overrideprotected void afterExecute(Runnable r, Throwable t) {super.afterExecute(r, t);if (r instanceof FutureTask<?>) {try {//get()的时候会将异常内的异常抛出((FutureTask<?>) r).get();} catch (InterruptedException e) {e.printStackTrace();Thread.currentThread().interrupt();} catch (ExecutionException e) {log.error("捕获到线程的异常返回值", e);}}//Throwable t永远为null,拿不到异常信息//log.error("afterExecute中捕获到异常,", t);}};threadPoolExecutor.execute(new Runnable() {@Overridepublic void run() {throw new RuntimeException("execute()发生异常");}});TimeUnit.MILLISECONDS.sleep(200L);threadPoolExecutor.submit(new Runnable() {@Overridepublic void run() {throw new RuntimeException("submit.run()发生异常");}});TimeUnit.MILLISECONDS.sleep(200L);threadPoolExecutor.submit(new Callable<String>() {@Overridepublic String call() throws Exception {throw new RuntimeException("submit.call()发生异常");}}).get(); //get()的时候会将异常抛出threadPoolExecutor.shutdown();}
}
欢迎在评论区留下你看文章时的思考,及时说出,有助于加深记忆和理解,还能和像你一样也喜欢这个话题的读者相遇~
# 本文源代码
https://github.com/FutaoSmile/learn-thread/tree/master/src/main/java/com/futao/learn/threads/捕获线程异常
Java多线程:捕获线程异常相关推荐
- Java并发-UncaughtExceptionHandler捕获线程异常信息并重新启动线程
Java并发-UncaughtExceptionHandler捕获线程异常信息并重新启动线程 参考文章: (1)Java并发-UncaughtExceptionHandler捕获线程异常信息并重新启动 ...
- Java多线程之线程池的手写改造和拒绝策略
Java多线程之线程池的手写改造和拒绝策略 目录 自定义线程池的使用 四种拒绝策略代码体现 1. 自定义线程池的使用 自定义线程池(拒绝策略默认AbortPolicy) public class My ...
- Java多线程之线程池7大参数、底层工作原理、拒绝策略详解
Java多线程之线程池7大参数详解 目录 企业面试题 线程池7大参数源码 线程池7大参数详解 底层工作原理详解 线程池的4种拒绝策略理论简介 面试的坑:线程池实际中使用哪一个? 1. 企业面试题 蚂蚁 ...
- UncaughtExceptionHandler:java未捕获的异常/错误默认处理方式
UncaughtExceptionHandler:java未捕获的异常/错误 @(JAVA)[java] java有一部分异常属于运行时异常,这类异常发生时通过会导致程序发生错误,导致退出.如数据下标 ...
- Java多线程:线程安全和非线程安全的集合对象
转载自 Java多线程:线程安全和非线程安全的集合对象 一.概念: 线程安全:就是当多线程访问时,采用了加锁的机制:即当一个线程访问该类的某个数据时,会对这个数据进行保护,其他线程不能对其访问,直到 ...
- java多线程与线程间通信
转自(http://blog.csdn.net/jerrying0203/article/details/45563947) 本文学习并总结java多线程与线程间通信的原理和方法,内容涉及java线程 ...
- Java多线程与线程并发库高级应用笔记
以下内容是学习张老师Java多线程与线程并发库高级应用时所做的笔记,很有用 网络编辑器直接复制Word文档排版有点乱,提供原始文件下载 先看源文件概貌 张孝祥_Java多线程与并发库高级应用 [视频介 ...
- Java多线程02(线程安全、线程同步、等待唤醒机制)
Java多线程2(线程安全.线程同步.等待唤醒机制.单例设计模式) 1.线程安全 如果有多个线程在同时运行,而这些线程可能会同时运行这段代码.程序每次运行结果和单线程运行的结果是一样的,而且其他的变量 ...
- Java多线程之线程池配置合理线程数
Java多线程之线程池配置合理线程数 目录 代码查看公司服务器或阿里云是几核的 合理线程数配置之CPU密集型 合理线程数配置之IO密集型 1. 代码查看公司服务器或阿里云是几核的 要合理配置线程数首先 ...
最新文章
- 谈谈Python那些不为人知的冷知识(一)
- Django博客系统(文章模型)
- OSI参考模型和网络编程
- 37个JavaScript基本面试问题和解答
- fopen php 乱码,如何解决php fgets读取文件乱码的问题
- React开发(125):ant design学习指南之form中的hasFeedback
- 三款新功能发布,助力阿里云表格存储再次升级
- SQL Server 2000/2005 数据库分页
- python 监视文件目录
- cl_salv_table
- k8s Deployment
- Hadoop集群部署模型纵览3
- HDU 1260: Tickets
- 如何将m3u8格式转成MP4以及可播放格式
- Zookeeper序列化及通信协议
- AS问题解决 Not recognizing known sRGB profile
- Self Service Password部署
- ArcGIS中的土地利用变化分析
- 黑马程序员--IDEA版本2018Java基础班+就业班大牛编程吧
- 分布式任务调度中心xxl-job