如何動態除錯執行緒池?

👉 這是一個或許對你有用的社群
🐱 一對一交流/面試小冊/簡歷最佳化/求職解惑,歡迎加入芋道快速開發平臺知識星球。下面是星球提供的部分資料:
👉這是一個或許對你有用的開源專案
國產 Star 破 10w+ 的開源專案,前端包括管理後臺 + 微信小程式,後端支援單體和微服務架構。
功能涵蓋 RBAC 許可權、SaaS 多租戶、資料許可權、商城、支付、工作流、大屏報表、微信公眾號、ERPCRMAI 大模型等等功能:
  • Boot 多模組架構:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • Cloud 微服務架構:https://gitee.com/zhijiantianya/yudao-cloud
  • 影片教程:https://doc.iocoder.cn
【國內首批】支援 JDK 17/21 + SpringBoot 3.3、JDK 8/11 + Spring Boot 2.7 雙版本 

這是有小夥伴最近在面深信服的時候遇到的一個問題,感覺比較有意思,今天和大夥來聊一聊。
如何動態除錯執行緒池?
面試官表示設定執行緒池核心執行緒數是一個非常具有挑戰性的事情,問有無辦法能夠動態的設定執行緒池核心數,並觀察其執行效果?
這個問題的難點在於它涉及到的技術點不是特別常用,該小夥伴面試的技術團隊剛好是做運維工具的,做一些監控軟體,所以剛好就問到這裡。
那麼和大家簡單聊一聊這個話題。
其實這裡主要是涉及到 Java 裡邊一個比較古老的工具,JMX。

一 什麼是 JMX

JMX(Java Management Extensions)是 Java 平臺的一部分,它提供了一種管理和監控 Java 應用程式的標準方法。JMX 允許你監控和管理系統資源、應用程式和服務,以及獲取關於這些實體的執行時資訊。
簡單來說,就是透過 JMX 可以動態檢視物件的執行資訊,並且可以動態修改物件屬性。
JMX 架構如下圖:

分析這張圖我們可以發現,JMX 底層是由很多不同的 MBeans 組成的,MBeans 是 JMX 的核心,它們是實現了特定介面的 Java 物件,用於表示可以被監控和管理的資源。MBeans 可以分為四種不同的型別,分別是:
  • Standard MBeans
  • Dynamic MBeans
  • Open MBeans
  • Model MBeans
這些 MBeans 的作用就是獲取物件的資訊,或者是修改物件資訊,都是透過 MBeans 來完成的。
所有的 MBeans 都需要註冊到 MBeanServer 上,然後再透過一些外部工具如 JMX、Web 瀏覽器等等,就可以去獲取或者修改 MBeans 的資訊了。
這裡的 MBean Server 是一個代理,它提供了一個註冊、檢索和操作 MBeans 的 API。它是 JMX 架構中的核心元件,負責管理所有 MBeans 的生命週期。
基於 Spring Boot + MyBatis Plus + Vue & Element 實現的後臺管理系統 + 使用者小程式,支援 RBAC 動態許可權、多租戶、資料許可權、工作流、三方登入、支付、簡訊、商城等功能
  • 專案地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 影片教程:https://doc.iocoder.cn/video/

二 程式碼實踐

接下來松哥透過一個簡單的案例,來和大家演示一下如何透過 JMX + jconsole 工具實現動態修改執行緒池資訊。
首先我們先來自定義一個動態執行緒池:
publicclassDynamicThreadPool{private ThreadPoolExecutor threadPoolExecutor;publicDynamicThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue){        threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);    }public ThreadPoolExecutor getThreadPoolExecutor(){return threadPoolExecutor;    }publicvoidsetCorePoolSize(int corePoolSize){        threadPoolExecutor.setCorePoolSize(corePoolSize);    }publicvoidsetMaximumPoolSize(int maximumPoolSize){        threadPoolExecutor.setMaximumPoolSize(maximumPoolSize);    }}
這個動態執行緒池實際上就是把我們傳統的執行緒池物件 ThreadPoolExecutor 封裝了一下,並且提供了兩個方法 setCorePoolSize 和 setMaximumPoolSize,透過這兩個方法我們可以動態設定執行緒池的執行緒數。
接下來我們自定義一個 MBean 介面,這個介面中提供四個方法,分別用來獲取或者設定執行緒數的資訊。
publicinterfaceDynamicThreadPoolMXBean{intgetCorePoolSize();voidsetCorePoolSize(int corePoolSize);intgetMaximumPoolSize();voidsetMaximumPoolSize(int maximumPoolSize);}
最後,我們自定義類實現 DynamicThreadPoolMXBean 介面,並繼承 StandardMBean 類,如下:
publicclassDynamicThreadPoolMBeanextendsStandardMBeanimplementsDynamicThreadPoolMXBean{private DynamicThreadPool dynamicThreadPool;publicDynamicThreadPoolMBean(DynamicThreadPool dynamicThreadPool)throws Exception {super(DynamicThreadPoolMXBean.class);this.dynamicThreadPool = dynamicThreadPool;        registerMBean();    }privatevoidregisterMBean(){try {            MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();            ObjectName name = new ObjectName("org.javaboy:type=DynamicThreadPool");            mbs.registerMBean(this, name);        } catch (Exception e) {            e.printStackTrace();        }    }@OverridepublicintgetCorePoolSize(){return dynamicThreadPool.getThreadPoolExecutor().getCorePoolSize();    }@OverridepublicvoidsetCorePoolSize(int corePoolSize){        dynamicThreadPool.setCorePoolSize(corePoolSize);    }@OverridepublicintgetMaximumPoolSize(){return dynamicThreadPool.getThreadPoolExecutor().getMaximumPoolSize();    }@OverridepublicvoidsetMaximumPoolSize(int maximumPoolSize){        dynamicThreadPool.setMaximumPoolSize(maximumPoolSize);    }}
這個類也沒啥神奇的地方,唯一要注意的是,在構造器中,我們呼叫了 registerMBean 方法,這個方法用來將當前物件註冊到 MBeanServer 上。
最後,我們就可以啟動自己的這段程式碼了:
publicclassMain{publicstaticvoidmain(String[] args)throws Exception {        DynamicThreadPool dynamicThreadPool = new DynamicThreadPool(240L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(10));        DynamicThreadPoolMBean mBean = new DynamicThreadPoolMBean(dynamicThreadPool);while (true) {            System.out.println("CorePoolSize:" + dynamicThreadPool.getThreadPoolExecutor().getCorePoolSize());            System.out.println("MaximumPoolSize:" + dynamicThreadPool.getThreadPoolExecutor().getMaximumPoolSize());try {                Thread.sleep(2000);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}
為了看到執行緒池的執行緒數量,我這裡使用了一個死迴圈不停的列印執行緒數量資訊,這樣一會透過 jconsole 修改執行緒池資訊的時候,我們就能看到修改的效果了。
程式啟動之後,我們使用 jconsole 連線上當前應用程式,如下圖:

在 MBeans 這個選項卡位置,我們可以看到剛剛配置的 MBean,右側的 value 則可以直接修改,修改之後,回到應用程式控制臺,我們會發現執行緒相關資料已經發生變化了。

可以看到,控制檯資訊已經發生變化。

歡迎加入我的知識星球,全面提升技術能力。
👉 加入方式,長按”或“掃描”下方二維碼噢
星球的內容包括:專案實戰、面試招聘、原始碼解析、學習路線。

文章有幫助的話,在看,轉發吧。
謝謝支援喲 (*^__^*)

相關文章