这是有小伙伴最近在面深信服的时候遇到的一个问题,感觉比较有意思,今天和大伙来聊一聊。
❝
如何动态调试线程池?
面试官表示设置线程池核心线程数是一个非常具有挑战性的事情,问有无办法能够动态的设置线程池核心数,并观察其执行效果?
这个问题的难点在于它涉及到的技术点不是特别常用,该小伙伴面试的技术团队刚好是做运维工具的,做一些监控软件,所以刚好就问到这里。
那么和大家简单聊一聊这个话题。
其实这里主要是涉及到 Java 里边一个比较古老的工具,JMX。
JMX(Java Management Extensions)是 Java 平台的一部分,它提供了一种管理和监控 Java 应用程序的标准方法。JMX 允许你监控和管理系统资源、应用程序和服务,以及获取关于这些实体的运行时信息。
❝
简单来说,就是通过 JMX 可以动态查看对象的运行信息,并且可以动态修改对象属性。
JMX 架构如下图:
分析这张图我们可以发现,JMX 底层是由很多不同的 MBeans 组成的,MBeans 是 JMX 的核心,它们是实现了特定接口的 Java 对象,用于表示可以被监控和管理的资源。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 工具实现动态修改线程池信息。
首先我们先来自定义一个动态线程池:
public class DynamicThreadPool { private ThreadPoolExecutor threadPoolExecutor; public DynamicThreadPool (int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue) { threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); } public ThreadPoolExecutor getThreadPoolExecutor () { return threadPoolExecutor; } public void setCorePoolSize (int corePoolSize) { threadPoolExecutor.setCorePoolSize(corePoolSize); } public void setMaximumPoolSize (int maximumPoolSize) { threadPoolExecutor.setMaximumPoolSize(maximumPoolSize); } }
这个动态线程池实际上就是把我们传统的线程池对象 ThreadPoolExecutor 封装了一下,并且提供了两个方法 setCorePoolSize 和 setMaximumPoolSize,通过这两个方法我们可以动态设置线程池的线程数。
接下来我们自定义一个 MBean 接口,这个接口中提供四个方法,分别用来获取或者设置线程数的信息。
public interface DynamicThreadPoolMXBean { int getCorePoolSize () ; void setCorePoolSize (int corePoolSize) ; int getMaximumPoolSize () ; void setMaximumPoolSize (int maximumPoolSize) ; }
最后,我们自定义类实现 DynamicThreadPoolMXBean 接口,并继承 StandardMBean 类,如下:
public class DynamicThreadPoolMBean extends StandardMBean implements DynamicThreadPoolMXBean { private DynamicThreadPool dynamicThreadPool; public DynamicThreadPoolMBean (DynamicThreadPool dynamicThreadPool) throws Exception { super (DynamicThreadPoolMXBean.class ) ; this .dynamicThreadPool = dynamicThreadPool; registerMBean(); } private void registerMBean () { try { MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); ObjectName name = new ObjectName("org.javaboy:type=DynamicThreadPool" ); mbs.registerMBean(this , name); } catch (Exception e) { e.printStackTrace(); } } @Override public int getCorePoolSize () { return dynamicThreadPool.getThreadPoolExecutor().getCorePoolSize(); } @Override public void setCorePoolSize (int corePoolSize) { dynamicThreadPool.setCorePoolSize(corePoolSize); } @Override public int getMaximumPoolSize () { return dynamicThreadPool.getThreadPoolExecutor().getMaximumPoolSize(); } @Override public void setMaximumPoolSize (int maximumPoolSize) { dynamicThreadPool.setMaximumPoolSize(maximumPoolSize); } }
这个类也没啥神奇的地方,唯一要注意的是,在构造器中,我们调用了 registerMBean 方法,这个方法用来将当前对象注册到 MBeanServer 上。
最后,我们就可以启动自己的这段代码了:
public class