Java線程池的使用及工做原理

前言

在平常開發過程當中老是以單線程的思惟去編碼,沒有考慮到在多線程狀態下的運行情況。由此引起的結果就是請求過多,應用沒法響應。爲了解決請求過多的問題,又衍生出了線程池的概念。經過「池」的思想,從而合理的處理請求。本文記錄了Java中線程池的使用及工做原理,若有錯誤,歡迎指正。html

什麼是線程池?

線程池是一種用於實現計算機程序併發執行的軟件設計模式。線程池維護多個線程,等待由調度程序分配任務以併發執行,該模型提升了性能,並避免了因爲爲短時間任務頻繁建立和銷燬線程而致使的執行延遲。

線程池要解決什麼問題?

說到線程池就必定要從線程的生命週期講起。java

![](/img/bVcSinY)git

從圖中能夠了解不管任務執行多久,每一個線程都要經歷從生到死的狀態。而使用線程池就是爲了不線程的重複建立,從而節省了線程的NewRunnableRunningTerminated的時間;同時也會複用線程,最小化的節省系統資源,於此同時提升了響應速度。github

線程池的使用

線程池的建立

使用ThreadPoolExecutor並配置7個參數完成線程池的建立web

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
  • corePoolSize:線程池中核心線程的最大值
  • maximumPoolSize:線程池中最大線程數
  • keepAliveTime:非核心線程空閒的存活時間大小
  • unit:keepAliveTime的單位,經常使用的有秒、分鐘、小時等
  • workQueue:阻塞隊列類型
  • threadFactory:線程工廠,用於配置線程的名稱,是否爲守護線程等
  • handler:線程池的拒絕策略

經常使用阻塞隊列

ArrayBlockingQueue

底層基於數組的實現的有界阻塞隊列面試

LinkedBlockingQueue

底層基於單鏈表的阻塞隊列,可配置容量,不配置容量默認爲Integer.MAX_VALUE設計模式

線程工廠

《阿里巴巴Java開發手冊》中強制要求指定線程的名稱
![](/img/bVcSinX)數組

因爲工做是使用hutool比較多,裏面也包含對ThreadFactory的封裝,能夠很方便的指定名稱多線程

ThreadFactory threadFactory = ThreadFactoryBuilder.create().setNamePrefix("myThread-").build();

拒絕策略

當線程池內工做線程數大於maximumPoolSize時,線程就再也不接受任務,執行對應的拒絕策略;目前支持的拒絕策略有四種:併發

  1. AbortPolicy(默認):丟棄任務並拋出RejectedExecutionException異常
  2. CallerRunsPolicy:由調用者處理
  3. DiscardOldestPolicy:丟棄隊列中最前面的任務,並從新入隊列
  4. DiscardPolicy:丟棄任務但不拋出異常

線程池的執行邏輯

// 建立線程工廠
ThreadFactory threadFactory = ThreadFactoryBuilder.create().setNamePrefix("myThread-").build();
// 建立線程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100), threadFactory, new ThreadPoolExecutor.AbortPolicy());

execute()方法

// 組合值;保存了線程池的工做狀態和工做線程數
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
public void execute(Runnable command) {
        // 任務爲空 拋出NPE
        if (command == null)
            throw new NullPointerException();
        // 獲取線程池狀態
        int c = ctl.get();
        // 若是工做線程數小於核心線程數就建立新線程
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        // 若是線程池處於Running狀態,就把任務放在隊列尾部
        if (isRunning(c) && workQueue.offer(command)) {
            // 從新檢查線程池狀態
            int recheck = ctl.get();
            // 若是線程池不是Running狀態,就移除剛纔添加的任務,並執行拒絕策略
            if (! isRunning(recheck) && remove(command))
                reject(command);
            // 是Running狀態,就添加線程
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        // 添加任務失敗,執行拒絕策略
        else if (!addWorker(command, false))
            reject(command);
    }
// addWorker()完成線程的建立

執行流程

參考文章:

  1. 面試必備:Java線程池解析 (juejin.cn)
  2. 別再說你不懂線程池——作個優雅的攻城獅 (juejin.cn)
  3. Java線程池實現原理及其在美團業務中的實踐 - 美團技術團隊 (meituan.com)

閱讀原文

相關文章
相關標籤/搜索