<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          手寫一個輕量級動態(tài)線程池,很香!!

          共 7924字,需瀏覽 16分鐘

           ·

          2023-01-07 18:00

          ?? 歡迎準備 Java 面試以及學習 Java 的同學加入我的? 知識星球 ?,干貨很多!收費雖然是白菜價,但星球里的內(nèi)容或許比你參加上萬的培訓班質(zhì)量還要高。

          ??? 《Java 面試指北》 ?來啦!這是一份教你如何更高效地準備面試的小冊,涵蓋常見八股文(系統(tǒng)設計、常見框架、分布式、高并發(fā) ......)、優(yōu)質(zhì)面經(jīng)等內(nèi)容。

          Java面試指南網(wǎng)站:javaguide.cn

          在后臺開發(fā)中,會經(jīng)常用到線程池技術,對于線程池核心參數(shù)的配置很大程度上依靠經(jīng)驗。然而,由于系統(tǒng)運行過程中存在的不確定性,我們很難一勞永逸地規(guī)劃一個合理的線程池參數(shù)。在對線程池配置參數(shù)進行調(diào)整時,一般需要對服務進行重啟,這樣修改的成本就會偏高。一種解決辦法就是,將線程池的配置放到平臺側(cè),運行開發(fā)同學根據(jù)系統(tǒng)運行情況對核心參數(shù)進行動態(tài)配置。

          本文以 Nacos 作為服務配置中心,以修改線程池核心線程數(shù)、最大線程數(shù)為例,實現(xiàn)一個簡單的動態(tài)化線程池。

          說明:實際項目中,我們可直接使用現(xiàn)成的輪子比如 Hippo4J、Dynamic Tp,沒必要自己手動實現(xiàn)。這篇文章的目的主要是為了讓大家搞懂可以動態(tài)修改線程池參數(shù)配置的原理。

          Hippo4J 我曾經(jīng)在「優(yōu)質(zhì) Java 開源項目推薦第 12 期」推薦過,Dynamic Tp 我曾經(jīng)在「優(yōu)質(zhì) Java 開源項目推薦第 13 期」推薦過。

          代碼實現(xiàn)

          1.依賴

                
                <dependency>
          ????<groupId>com.alibaba.cloud</groupId>
          ????<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
          ????<version>2021.1</version>
          </dependency>
          <dependency>
          ????<groupId>com.alibaba.cloud</groupId>
          ????<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
          ????<version>2021.1</version>
          </dependency>
          <dependency>
          ????<groupId>org.springframework.boot</groupId>
          ????<artifactId>spring-boot-starter-web</artifactId>
          </dependency>
          <dependency>
          ????<groupId>org.springframework.boot</groupId>
          ????<artifactId>spring-boot-starter</artifactId>
          </dependency>

          2.配置 yml 文件

          bootstrap.yml

                
                server:
          ??port:?8010
          ??#?應用名稱(nacos會將該名稱當做服務名稱)
          spring:
          ??application:
          ????name:?order-service
          ??cloud:
          ????nacos:
          ??????discovery:
          ????????namespace:?public
          ????????server-addr:?192.168.174.129:8848
          ??????config:
          ????????server-addr:?192.168.174.129:8848
          ????????file-extension:?yml

          application.yml

                
                spring:
          ??profiles:
          ????active:?dev

          為什么要配置兩個 yml 文件?

          springboot 中配置文件的加載是存在優(yōu)先級順序的,bootstrap 優(yōu)先級高于 application。

          nacos 在項目初始化時,要保證先從配置中心進行配置拉取,拉取配置之后才能保證項目的正常啟動。

          3.nacos 配置

          登錄到 nacos 管理頁面,新建配置,如下圖所示:

          4fdc882d4b415a1230f4ac4b584ba4d7.webp

          注意 Data ID 的命名格式為,${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension},在本文中,Data ID 的名字就是order-service-dev.yml。

          82ea34ded4dcc7da9aef8362fda4407b.webp

          這里我們只配置了兩個參數(shù),核心線程數(shù)量和最大線程數(shù)。

          4.線程池配置和 nacos 配置變更監(jiān)聽

                
                @RefreshScope
          @Configuration
          public?class?DynamicThreadPool?implements?InitializingBean?{
          ????@Value("${core.size}")
          ????private?String?coreSize;

          ????@Value("${max.size}")
          ????private?String?maxSize;

          ????private?static?ThreadPoolExecutor?threadPoolExecutor;

          ????@Autowired
          ????private?NacosConfigManager?nacosConfigManager;

          ????@Autowired
          ????private?NacosConfigProperties?nacosConfigProperties;

          ????@Override
          ????public?void?afterPropertiesSet()?throws?Exception?{
          ????????//按照nacos配置初始化線程池
          ????????threadPoolExecutor?=?new?ThreadPoolExecutor(Integer.parseInt(coreSize),?Integer.parseInt(maxSize),?10L,?TimeUnit.SECONDS,
          ????????????????new?LinkedBlockingQueue<>(10),
          ????????????????new?ThreadFactoryBuilder().setNameFormat("c_t_%d").build(),
          ????????????????new?RejectedExecutionHandler()?{
          ????????????????????@Override
          ????????????????????public?void?rejectedExecution(Runnable?r,?ThreadPoolExecutor?executor)?{
          ????????????????????????System.out.println("rejected!");
          ????????????????????}
          ????????????????});

          ????????//nacos配置變更監(jiān)聽
          ????????nacosConfigManager.getConfigService().addListener("order-service-dev.yml",?nacosConfigProperties.getGroup(),
          ????????????????new?Listener()?{
          ????????????????????@Override
          ????????????????????public?Executor?getExecutor()?{
          ????????????????????????return?null;
          ????????????????????}

          ????????????????????@Override
          ????????????????????public?void?receiveConfigInfo(String?configInfo)?{
          ????????????????????????//配置變更,修改線程池配置
          ????????????????????????System.out.println(configInfo);
          ????????????????????????changeThreadPoolConfig(Integer.parseInt(coreSize),?Integer.parseInt(maxSize));
          ????????????????????}
          ????????????????});
          ????}

          ????/**
          ?????*?打印當前線程池的狀態(tài)
          ?????*/

          ????public?String?printThreadPoolStatus()?{
          ????????return?String.format("core_size:%s,thread_current_size:%s;"?+
          ????????????????????????"thread_max_size:%s;queue_current_size:%s,total_task_count:%s",?threadPoolExecutor.getCorePoolSize(),
          ????????????????threadPoolExecutor.getActiveCount(),?threadPoolExecutor.getMaximumPoolSize(),?threadPoolExecutor.getQueue().size(),
          ????????????????threadPoolExecutor.getTaskCount());
          ????}

          ????/**
          ?????*?給線程池增加任務
          ?????*
          ?????*?@param?count
          ?????*/

          ????public?void?dynamicThreadPoolAddTask(int?count)?{
          ????????for?(int?i?=?0;?i?<?count;?i++)?{
          ????????????int?finalI?=?i;
          ????????????threadPoolExecutor.execute(new?Runnable()?{
          ????????????????@Override
          ????????????????public?void?run()?{
          ????????????????????try?{
          ????????????????????????System.out.println(finalI);
          ????????????????????????Thread.sleep(10000);
          ????????????????????}?catch?(InterruptedException?e)?{
          ????????????????????????e.printStackTrace();
          ????????????????????}
          ????????????????}
          ????????????});
          ????????}
          ????}

          ????/**
          ?????*?修改線程池核心參數(shù)
          ?????*
          ?????*?@param?coreSize
          ?????*?@param?maxSize
          ?????*/

          ????private?void?changeThreadPoolConfig(int?coreSize,?int?maxSize)?{
          ????????threadPoolExecutor.setCorePoolSize(coreSize);
          ????????threadPoolExecutor.setMaximumPoolSize(maxSize);
          ????}
          }

          這個代碼就是實現(xiàn)動態(tài)線程池和核心了,需要說明的是:

          • @RefreshScope:這個注解用來支持 nacos 的動態(tài)刷新功能;
          • @Value("${max.size}")@Value("${core.size}"):這兩個注解用來讀取我們上一步在 nacos 配置的具體信息;同時,nacos 配置變更時,能夠?qū)崟r讀取到變更后的內(nèi)容
          • nacosConfigManager.getConfigService().addListener:配置監(jiān)聽,nacos 配置變更時實時修改線程池的配置。

          5.controller

          為了觀察線程池動態(tài)變更的效果,增加 Controller 類。

                
                @RestController
          @RequestMapping("/threadpool")
          public?class?ThreadPoolController?{

          ????@Autowired
          ????private?DynamicThreadPool?dynamicThreadPool;

          ????/**
          ?????*?打印當前線程池的狀態(tài)
          ?????*/

          ????@GetMapping("/print")
          ????public?String?printThreadPoolStatus()?{
          ????????return?dynamicThreadPool.printThreadPoolStatus();
          ????}

          ????/**
          ?????*?給線程池增加任務
          ?????*
          ?????*?@param?count
          ?????*/

          ????@GetMapping("/add")
          ????public?String?dynamicThreadPoolAddTask(int?count)?{
          ????????dynamicThreadPool.dynamicThreadPoolAddTask(count);
          ????????return?String.valueOf(count);
          ????}
          }

          6.測試

          啟動項目,訪問http://localhost:8010/threadpool/print打印當前線程池的配置。

          b3b28565aa899ecb2068163bf1ac23e0.webp圖片

          可以看到,這個就是我們之前在 nacos 配置的線程數(shù)。

          訪問http://localhost:8010/threadpool/add?count=20增加20個任務,重新打印線程池配置

          f006534988490cbd3681ac4ec8611986.webp圖片

          可以看到已經(jīng)有線程在排隊了。

          為了能夠看到效果,我們多訪問幾次/add 接口,增加任務數(shù),在控制臺出現(xiàn)拒絕信息時調(diào)整 nacos 配置。

          4debce6b96f718da18d5c2e868a10398.webp

          此時,執(zhí)行/add 命令時,所有的線程都會提示 rejected。

          調(diào)整 nacos 配置,將核心線程數(shù)調(diào)整為 50,最大線程數(shù)調(diào)整為 100.

          9840c13f92b035b9183f1e759a6f10bb.webp

          重新多次訪問/add 接口增加任務,發(fā)現(xiàn)沒有拒絕信息了。這時,打印具體的線程狀態(tài),發(fā)現(xiàn)線程池參數(shù)修改成功。

          fa2f036e234320ed0fd0ee78914b3ea3.webp圖片

          總結(jié)

          這里,只是簡單實現(xiàn)了一個可以調(diào)整核心線程數(shù)和最大線程數(shù)的動態(tài)線程池。具體的線程池實現(xiàn)原理可以參考美團的這篇文章:https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html,結(jié)合監(jiān)控告警等實現(xiàn)一個完善的動態(tài)線程池產(chǎn)品。

          優(yōu)秀的輪子還有好多,比如 Hippo4J ,使用起來和 dynamic-tp 差不多。Hippo4J 有無依賴中間件實現(xiàn)動靜線程池,也有默認實現(xiàn) Nacos 和 Apollo 的版本,而 dynamic-tp 默認實現(xiàn)依賴 Nacos 或 Apollo。

          ·········? END? ··············

          ?? 歡迎準備 Java 面試以及學習 Java 的同學加入我的 知識星球 ,干貨很多!收費雖然是白菜價,但星球里的內(nèi)容或許比你參加上萬的培訓班質(zhì)量還要高。

          ??? 《Java 面試指北》 來啦!這是一份教你如何更高效地準備面試的小冊,涵蓋常見八股文(系統(tǒng)設計、常見框架、分布式、高并發(fā) ......)、優(yōu)質(zhì)面經(jīng)等內(nèi)容。

          近期文章精選?:

          ??如果本文對你有幫助的話,歡迎?點贊&在看&分享?,這對我繼續(xù)分享&創(chuàng)作優(yōu)質(zhì)文章非常重要。非常感謝!

          瀏覽 104
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  草逼小视频无码 | 天天爱夜夜爱 | 操逼视频素材大全网站直接看 | 久久久精品无码 | 天天操天天日天天搞天天爽天天干 |