zoukankan      html  css  js  c++  java
  • ThreadPoolExcutor 线程池 异常处理 (上篇)

    前言

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

    示例代码

    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>());
    
            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;
                    }
                }
            }
        }
    }
    

    上面的代码是原作者本地调试的一个代码,这里我也大致交代下情形:

    1. 首先是启动main方法看最终执行现象

    这里直接抛异常了,by zero。看到底层是ThreadPoolExecutor 1149行抛出的。
    查看线程dump,发现线程池中的线程此时处于WAITING状态

    1. 源码追踪
      这里就需要弄清楚为何会出现WAITING状态,所以我们需要一步步追踪源码。
      我们可以在抛异常的地方打断点,然后一步步跟踪:

    在执行1149行代码由于抛了异常,所以继续执行finally中processWorkerExit方法:

    processWorkerExit中主要做了两件事,worker remover和addWorker。线程池中的任务都会被包装为一个内部 Worker 对象执行。不清楚的可以参考:Java并发之线程池ThreadPoolExecutor源码学习

    最后会执行addWorker,紧接着我们继续往addWorker中去跟,看看里面做了什么操作:

    addWorker里面是重新new Worker(), 然后执行worker.start(), 接着我们看下Worker中的start方法:

    因为Woker是实现Runnable接口的,所以会执行其run方法,接着往runWorker方法跟踪:

    因为此时的Worker是上一步重新new出来的,所以其中的task为空,这时需要继续跟踪getTask()方法:

    此时因为线程池的队列中并没有任务,所以这里执行take会一直阻塞,也就有了最开始的那个WAITING的状态了。
    到了这里一切都很明了了,源码面前任何妖魔鬼怪都无法藏匿,所以但我们使用线程池的时候一定要注意一异常的捕获和处理。
    下一章来详细解读一下如何捕获线程池中的异常。

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

  • 相关阅读:
    tushare包使用案例
    Matplotlib模块:绘图和可视化
    pandas使用
    django 表操作
    元数据Meta
    django关系类型字段
    django项目模型字段
    django项目mysite 2
    django安装使用xadmin
    GCC版本中没有GLIBCXX_3.4.15错误
  • 原文地址:https://www.cnblogs.com/wang-meng/p/10588637.html
Copyright © 2011-2022 走看看