前言

因为这是之前面试的一个题目,所以印象比较深刻,前几天写了一篇文章:ThreadPoolExcutor 线程池 异常处理 (上篇) 中已经介绍了线程池异常的一些问题以及一步步分析了里面的一些源代码,今天就来继续说下如何防范这种情况。

结论

这里直接抛出结论,然后再一个个分析:

  • 在我们提供的Runnable的run方法中捕获任务代码可能抛出的所有异常,包括未检测异常
  • 使用ExecutorService.submit执行任务,利用返回的Future对象的get方法接收抛出的异常,然后进行处理
  • 重写ThreadPoolExecutor.afterExecute方法,处理传递到afterExecute方法中的异常
  • 为工作者线程设置UncaughtExceptionHandler,在uncaughtException方法中处理异常 (不推荐)

分析解读

Runnable的run方法中捕获任务代码可能抛出的所有异常

这个其实最简单,但是往往面试官问这个问题 考察的点也不在这里。具体的方式可以参考我之前的一篇文章:论如何优雅的自定义ThreadPoolExecutor线程池

核心代码如下:

使用ExecutorService.submit执行任务,利用返回的Future对象的get方法接收抛出的异常

1, 使用submit执行异步任务,然后通过Future的get方法来接收异常。演示如下:

冲图片可以看到,使用了get方法后,这里直接接收到了异常信息。

2, 这里newTaskFor返回的是FutureTask,然后传递给了execute方法:

3, 接着我们继续往下跟踪execute方法,发现这里调用的是ThreadExecutor中的execute方法,在ThreadPoolExcutor 线程池 异常处理 (上篇) 我们已经分析过这里,最终会到addWorker方法中执行线程的start()方法,因为我们在上一张图片传递的是FutureTask, 所以我们继续跟踪FutureTask中的run方法:

4, 到了FutureTask.run() 方法中,一切似乎都已经明了,这里会有catch捕获当前线程抛出的异常,紧接着我们看看setException做了什么事情:

5,setExcetion首先是将一个异常信息赋值给一个全局变量outcome,并且将全局的任务状态state字段通过CAS更新为3(异常状态)

然后最后做一些清理工作。

6,finishCompletion后续是做一些线程池的清理工作,这里涉及到线程池以及线程池中的等待队列的操作,不清楚的同学可以看下线程池实现代码。到了这里线程池中的线程执行已经完毕了,下面再去跟踪一下FutureTask.get()方法。

7,这里是FutureTask.get()的底层实现,这里其实会拿上面的setException方法中设置的outcome和state做一些逻辑判断,到了这里就直接往上抛出了异常,所以我们在最开始的main方法中才能够捕获到这个异常。

重写ThreadPoolExecutor.afterExecute方法,处理传递到afterExecute方法中的异常

这里为何要重写afterExecute方法呢?因为线程执行完毕后一定会执行此方法,源码如下:

所以我们可以重写此方法来达到接收异常的目的。

为工作者线程设置UncaughtExceptionHandler,在uncaughtException方法中处理异常 (不推荐)

1,我们在之前ThreadExecutor->Worker->run方法中直接往上抛出了异常,但是这些异常抛到哪里了呢?

2,通过查询JVM的一些资料,最终的异常会到Thread.dispatchUncaughtException中,如下图:

3,所以当我们自定义UncaughtExceptionHandler时就可以捕获到

具体测试代码如下:

/**
* 测试singleThreadPool异常问题
*
* @author: wangmeng
* @date: 2019/3/25 23:40
*/
public class ThreadPoolException {
private final static Logger LOGGER = LoggerFactory.getLogger(ThreadPoolException.class); public static void main(String[] args) throws InterruptedException {
ExecutorService execute = new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), new ThreadFactoryBuilder().setUncaughtExceptionHandler(new MyHandler()).build()); execute.execute(new Runnable() {
@Override
public void run() {
LOGGER.info("=====11=======");
}
}); TimeUnit.SECONDS.sleep(5);
execute.execute(new Run1());
} private static class Run1 implements Runnable {
@Override
public void run() {
int count = 0;
while (true) {
count++;
LOGGER.info("-------222-------------{}", count); if (count == 10) {
System.out.println(1 / 0);
try {
} catch (Exception e) {
LOGGER.error("Exception",e);
}
} if (count == 20) {
LOGGER.info("count={}", count);
break;
}
}
}
}
} class MyHandler implements Thread.UncaughtExceptionHandler {
private final static Logger LOGGER = LoggerFactory.getLogger(MyHandler.class);
@Override
public void uncaughtException(Thread t, Throwable e) {
LOGGER.error("threadId = {}, threadName = {}, ex = {}", t.getId(), t.getName(), e.getMessage());
}
}

上面说了其实是不推荐重写UncaughtExceptionHandler 的,因为UncaughtExceptionHandler 只有在execute.execute()方法中才生效,在execute.submit中是无法捕获到异常的。

由于本人水平有限,文章中如果有不严谨的地方还请提出来,愿闻其详。

ThreadPoolExcutor 线程池 异常处理 (下篇)的更多相关文章

  1. ThreadPoolExcutor 线程池 异常处理 (上篇)

    前言 最近看到crossoverJie的一篇文章:一个线程罢工的诡异事件 首先感谢原作者的分享,自己获益匪浅.然后是回想到自己的一次面试经历,面试官提问了线程池中的线程出现了异常该怎样捕获?会导致什么 ...

  2. JDK线程池异常处理方式

    1. 抛出异常 execute() java.util.concurrent.ThreadPoolExecutor#runWorker 中抛出,抛出之后经过以下两个步骤: catch块捕获,捕获之后再 ...

  3. JAVA 线程池之Callable返回结果

    本文介绍如何向线程池提交任务,并获得任务的执行结果.然后模拟 线程池中的线程在执行任务的过程中抛出异常时,该如何处理. 一,执行具体任务的线程类 要想 获得 线程的执行结果,需实现Callable接口 ...

  4. 面试必备:Java线程池解析

    前言 掌握线程池是后端程序员的基本要求,相信大家求职面试过程中,几乎都会被问到有关于线程池的问题.我在网上搜集了几道经典的线程池面试题,并以此为切入点,谈谈我对线程池的理解.如果有哪里理解不正确,非常 ...

  5. Java线程池面试

    New Thread的弊端 每次new Thread会新建对象,性能差 线程缺乏统一管理,可能无限制的新建线程,相互竞争,有可能占用过多系统资源导致死机或OOM 缺少更多功能,如更多执行.定期执行.线 ...

  6. JAVA线程池ScheduledExecutorService周期性地执行任务 与单个Thread周期性执行任务的异常处理

    本文记录: 1,使用ScheduledExecutorService的 scheduleAtFixedRate 方法执行周期性任务的过程,讨论了在任务周期执行过程中出现了异常,会导致周期任务失败. 2 ...

  7. java 线程池第一篇 之 ThreadPoolExcutor

    一:什么是线程池? java 线程池是将大量的线程集中管理的类,包括对线程的创建,资源的管理,线程生命周期的管理.当系统中存在大量的异步任务的时候就考虑使用java线程池管理所有的线程.减少系统资源的 ...

  8. 线程池:ThreadPoolExcutor源码阅读

    ThreadPoolExcutor源码流程图:(图片较大,下载再看比较方便) 线程池里的二进制奥秘 前言: 线程池的五种状态state(RUNNING.SHUTDOWN.STOP.TIDYING.TE ...

  9. Java中线程池,你真的会用吗?ExecutorService ThreadPoolExcutor

    原文:https://www.hollischuang.com/archives/2888 在<深入源码分析Java线程池的实现原理>这篇文章中,我们介绍过了Java中线程池的常见用法以及 ...

随机推荐

  1. mybatis查询语句的背后之封装数据

    转载请注明出处... 一.前言 继上一篇mybatis查询语句的背后,这一篇主要围绕着mybatis查询的后期操作,即跟数据库交互的时候.由于本人也是一边学习源码一边记录,内容难免有错误或不足之处,还 ...

  2. A - Character Encoding HDU - 6397 - 方程整数解-容斥原理

    A - Character Encoding HDU - 6397 思路 : 隔板法就是在n个元素间的(n-1)个空中插入k-1个板,可以把n个元素分成k组的方法 普通隔板法 求方程 x+y+z=10 ...

  3. pyquery 库的方法

    初始化 在这里介绍四种初始化方式. (1)直接字符串 from pyquery import PyQuery as pq doc = pq("<html></html> ...

  4. 声明式调用---Feign

    Feign:Feign是一种声明式.模板化的HTTP客户端. 用我的理解来说,Feign的功能类似dubbo暴露服务,但是与dubbo稍有不同的是Feign是HTTP REST接口的形式暴露的. 这一 ...

  5. List自定义排序(可多条件)

    1:先建需要排序的属性Model package com.msqsoft.app.queuing.util; public class ListOrderByModel { private Strin ...

  6. [R] R dataframe 中对列使用sort或者order的注意

    存在这样的需求: 针对每列的值, 对列进行排序. 这样处理过数据后, 在excel中对数据作图时, 使数据呈现有序. R中sort数据的时候, 如果数据中存在字符串, R会将数据转化为characte ...

  7. AGC 030B.Tree Burning(贪心)

    题目链接 \(Description\) 有一个长为\(L\)的环,上面有\(n\)棵树,坐标分别为\(a_i\).初始时在原点. 每次你可以选择顺时针或逆时针走到第一棵没有被烧掉的树,停在这个位置, ...

  8. vue给methods中的方法传入当前点击行的值

    <template> <!-- 在template中,只能存在一个根组件 --> <div class="event"> <ul> ...

  9. C语言基础一(敲打键盘、寻找资料)

    事前声明一点:小编的所有材料都是基础,没有什么大的不同,您若觉得不错的话,可以互相探讨下,毕竟本人也是小雏鸟. 大家在学习C语言.C++类似的高端语言时候,往往都是为了学而学,殊不知为什么而学,或许更 ...

  10. CY7C68013 USB接口相机开发记录 - 第一天:资料下载

    一直觉得从头开发一套东西出来会极大的提升自己的自信心,能够最大化的开发自己的潜能.所以在犹豫很久之后决定学习下CY7C68013 USB接口相机的开发. 通过在网上查找多份资料后,觉得工欲善其事必先利 ...