成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

小米面試:什么是線程池?工作原理是什么?線程池可以動態修改嗎?

開發 前端
線程池(Thread Pool)是一種基于池化思想管理線程的工具,它維護多個線程。在線程池中,總有幾個活躍線程。當需要使用線程來執行任務時,可以從池子中隨便拿一個空閑線程來用,當完成工作時,該線程并不會死亡,而是再次返回線程池中成為空閑狀態,等待執行下一個任務。

大家好,我是碼哥,《Redis 高手心法》暢銷書作者。

有讀者分享小米 Java 后端面試,其中有一個問題,當時沒有回答好:什么是線程池、工作原理是什么、線程池可以動態修改嗎?

回答這個問題之前,首先我們來了解下什么是線程池,它的工作原理是什么。

什么是線程池

線程池(Thread Pool)是一種基于池化思想管理線程的工具,它維護多個線程。在線程池中,總有幾個活躍線程。當需要使用線程來執行任務時,可以從池子中隨便拿一個空閑線程來用,當完成工作時,該線程并不會死亡,而是再次返回線程池中成為空閑狀態,等待執行下一個任務。

這種做法,一方面避免了處理任務時創建銷毀線程開銷的代價,另一方面避免了線程數量膨脹導致的過分調度問題,保證了對內核的充分利用。

線程池狀態

然后,我們來看下線程池有哪些狀態呢?

線程池有五種狀態:這五種狀態并不能任意轉換,只會有以下幾種轉換情況:線程池的五種狀態是如何流轉的?

  • RUNNING:會接收新任務并且會處理隊列中的任務
  • SHUTDOWN:不會接收新任務并且會處理隊列中的任務
  • STOP:不會接收新任務并且不會處理隊列中的任務,并且會中斷在處理的任務(注意:一個任務能不能被中斷得看任務本身)
  • TIDYING:所有任務都終止了,線程池中也沒有線程了,這樣線程池的狀態就會轉為 TIDYING,一旦達到此狀態,就會調用線程池的 terminated()
  • TERMINATED:terminated()執行完之后就會轉變為 TERMINATED

圖片

線程池工作原理

如何自定義一個線程池?

public ThreadPoolExecutor threadPoolExecutor() {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                // 核心線程池大小,表示線程池常駐線程數量
                30,
                // 最大線程數,表示線程池最多創建的線程數量
                100,
                // 保活時間,表示一個非核心線程多久沒有使用,會被回收
                10,
                TimeUnit.MINUTES,
                // 阻塞隊列,表示隊列最多緩存多少任務,如果隊列滿了,將觸發 RejectedExecutionHandler
                new ArrayBlockingQueue<>(1000),
                // 線程工廠,創建線程時候用的,可以給線程命名等
                new NamedThreadFactory("cust-task")
        );
        // 拒絕策略,當阻塞隊列滿了之后,會觸發這里的handler
        // 默認是丟棄新任務
        executor.setRejectedExecutionHandler((r, executor1) -> {
            log.warn("thread pool is full");
        });
    }

線程池執行流程圖

圖片

  1. 首先檢測線程池運行狀態,如果不是 RUNNING,則直接拒絕,線程池要保證在 RUNNING 的狀態下執行任務。
  2. 如果當前線程數未超過核心線程數,則創建并啟動一個線程來執行新提交的任務。
  3. 如果當前線程數超過核心線程數,且線程池內的阻塞隊列未滿,則將任務添加到該阻塞隊列中。
  4. 如果當前線程數超過核心線程數且 線程池內的阻塞隊列已滿,且未超過最大線程數,則創建并啟動一個線程來執行新提交的任務。
  5. 如果已超過最大線程數,并且線程池內的阻塞隊列已滿, 則根據拒絕策略來處理該任務, 默認的處理方式是直接拋異常。

注意:提交一個 Runnable 時,不管當前線程池中的線程是否空閑,只要數量小于核心線程數就會創建新線程。

線程池的拒絕策略

圖片

ThreadPoolExecutor 內部有實現 4 個拒絕策略:

  1. CallerRunsPolicy,由調用 execute 方法提交任務的線程來執行這個任務。
  2. AbortPolicy,拋出異常 RejectedExecutionException 拒絕提交任務。
  3. DiscardPolicy,直接拋棄任務,不做任何處理。
  4. DiscardOldestPolicy,去除任務隊列中的第一個任務(最舊的),重新提。

如何監控線程池?

好了,言歸正傳,再回歸到這個題目本身,在修改線程池之前,我們要如何監控線程池的信息呢?

比如線程池的執行任務前后總時間,當前任務數等信息。

  • 統計任務執行時間可以通過實現 beforeExecute 和 afterExecute 方法,計算出任務總耗時。

圖片

  • 統計線程池的任務數,線程數等信息,可定時上報到 kafka,展示到可視化的界面上比如 Grafana。

圖片

監控核心代碼

@Slf4j
public class ThreadPoolMonitor {

    private final ThreadPoolExecutor customThreadPool;
    private final String poolName;
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    public ThreadPoolMonitor(ThreadPoolExecutor customThreadPool, String poolName) {
        this.customThreadPool = customThreadPool;
        this.poolName = poolName;
    }

    public void startMonitoring(long period, TimeUnit unit) {
        scheduler.scheduleAtFixedRate(this::monitor, 0, period, unit);
    }

    private void monitor() {
        //核心線程數
        int corePoolSize = customThreadPool.getCorePoolSize();
        //最大線程數
        int maximumPoolSize = customThreadPool.getMaximumPoolSize();
        //活躍線程數
        int activeCount = customThreadPool.getActiveCount();
        //隊列任務數
        int queueSize = customThreadPool.getQueue().size();
        //已執行完成任務數
        long completedTaskCount = customThreadPool.getCompletedTaskCount();
        //隊列任務數峰值
        int largestPoolSize = customThreadPool.getLargestPoolSize();

        //上報監控數據
        sendToKafka(corePoolSize,maximumPoolSize, activeCount, queueSize, completedTaskCount, largestPoolSize);
    }

    private void sendToKafka(int corePoolSize,int maximumPoolSize, int activeCount, int queueSize, long completedTaskCount, int largestPoolSize) {
        // 自定義實現發送kafka邏輯或上報到prometheus邏輯
    }
}

如何動態調整線程池?

一般我們在設置線程池的線程數時,會參考實際業務場景。比較通用的公式是

  • IO 密集型場景:線程數=CPU 核心數*2+1
  • CPU 密集型場景線程數=CPU 核心數+1

但這只是比較簡單粗暴的計算方式,在實際使用過程中,我們還是不可避免的需要調整線程池的一些參數,以達到最佳性能。

那么我們通過會比較關注線程池以下的幾個參數

線程池參數

說明

corePoolSize

核心線程數

maximumPoolSize

最大線程數

queueCapacity

等待隊列大小

keepAliveTime

空閑時間

  1. corePoolSize、maximumPoolSize 和 keepAliveTime 可以通過調用 setCorePoolSize、setMaximumPoolSize、setKeepAliveTime 方法修改。
  2. queueCapacity 雖然不能直接修改,我們可以通過實現自定義一個阻塞隊列的方式去實現 setQueueCapacity 方法來修改隊列大小的屬性。

最后可以通過 Apollo、Nacos 配置中心實現動態監聽的方法,達到實時更新線程池的效果。

擴展 1:線程池核心線程數會被銷毀嗎?

擴展 2:線程發生異常,會被移出線程池嗎?

責任編輯:武曉燕 來源: 碼哥跳動
相關推薦

2024-05-20 10:03:15

線程池優先級隊列排序方法

2024-07-15 08:20:24

2022-03-02 07:36:37

池化技術Java線程池

2024-03-11 18:18:58

項目Spring線程池

2013-08-27 14:04:29

2022-09-13 07:50:26

小米面試官MySQL

2021-07-16 11:35:20

Java線程池代碼

2020-12-10 08:24:40

線程池線程方法

2012-05-15 02:18:31

Java線程池

2024-11-27 08:15:50

2025-01-09 11:24:59

線程池美團動態配置中心

2021-02-05 12:34:33

線程池系統

2020-03-05 15:34:16

線程池C語言局域網

2020-04-29 14:10:44

Java線程池編程語言

2022-06-24 06:43:57

線程池線程復用

2022-03-21 07:40:08

線程池Executors方式

2023-07-28 07:18:39

final繼承結構

2023-05-19 08:01:24

Key消費場景

2022-03-14 08:02:08

輕量級動態線程池

2023-01-29 08:04:24

線程池非核心線程任務
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 在线观看成人免费视频 | 久久男女视频 | 日韩欧美在线观看 | 成年人在线播放 | 日韩午夜一区二区三区 | 日韩精品久久一区二区三区 | 国产精品久久久亚洲 | 丁香五月网久久综合 | 视频一区二区在线观看 | 正在播放国产精品 | 国产精品久久 | 精品亚洲一区二区三区四区五区 | 精品国产91亚洲一区二区三区www | 婷婷成人在线 | 偷拍自拍在线观看 | 免费黄色特级片 | 亚洲精品在线免费看 | 夜夜干夜夜操 | 九九九久久国产免费 | 成人片免费看 | 久久一区二区三区电影 | 老司机精品福利视频 | 国产精品毛片一区二区在线看 | 精品久久久久久亚洲精品 | a级免费视频 | 国产一区二区中文字幕 | 成人av一区| 性精品| 在线电影日韩 | 91精品国产色综合久久 | 2022精品国偷自产免费观看 | 亚洲一区在线日韩在线深爱 | 亚洲人成网亚洲欧洲无码 | 亚洲欧美中文日韩在线v日本 | 免费一级淫片aaa片毛片a级 | 日本大片在线播放 | 成人三级视频在线观看 | 亚洲 中文 欧美 日韩 在线观看 | 久久国内| 伊人天堂网 | 中文字幕av亚洲精品一部二部 |