ThreadPoolExcutor 線程池 異常處理 (上篇)

前言

最近看到crossoverJie的一篇文章:一個線程罷工的詭異事件
首先感謝原做者的分享,本身獲益匪淺。而後是回想到本身的一次面試經歷,面試官提問了線程池中的線程出現了異常該怎樣捕獲?會致使什麼樣的問題?html

示例代碼

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

上面的代碼是原做者本地調試的一個代碼,這裏我也大體交代下情形:java

  1. 首先是啓動main方法看最終執行現象

這裏直接拋異常了,by zero。看到底層是ThreadPoolExecutor 1149行拋出的。
查看線程dump,發現線程池中的線程此時處於WAITING狀態
面試

  1. 源碼追蹤
    這裏就須要弄清楚爲什麼會出現WAITING狀態,因此咱們須要一步步追蹤源碼。
    咱們能夠在拋異常的地方打斷點,而後一步步跟蹤:

在執行1149行代碼因爲拋了異常,因此繼續執行finally中processWorkerExit方法:
併發

processWorkerExit中主要作了兩件事,worker remover和addWorker。線程池中的任務都會被包裝爲一個內部 Worker 對象執行。不清楚的能夠參考:Java併發之線程池ThreadPoolExecutor源碼學習
ide

最後會執行addWorker,緊接着咱們繼續往addWorker中去跟,看看裏面作了什麼操做:
學習

addWorker裏面是從新new Worker(), 而後執行worker.start(), 接着咱們看下Worker中的start方法:
線程

由於Woker是實現Runnable接口的,因此會執行其run方法,接着往runWorker方法跟蹤:
調試

由於此時的Worker是上一步從新new出來的,因此其中的task爲空,這時須要繼續跟蹤getTask()方法:
code

此時由於線程池的隊列中並無任務,因此這裏執行take會一直阻塞,也就有了最開始的那個WAITING的狀態了。
到了這裏一切都很明瞭了,源碼面前任何妖魔鬼怪都沒法藏匿,因此但咱們使用線程池的時候必定要注意一異常的捕獲和處理。
下一章來詳細解讀一下如何捕獲線程池中的異常。htm

因爲本人水平有限,文章中若是有不嚴謹的地方還請提出來,願聞其詳。

相關文章
相關標籤/搜索