⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 江南一点雨 「江南一点雨」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

这是有小伙伴最近在面深信服的时候遇到的一个问题,感觉比较有意思,今天和大伙来聊一聊。

❝

如何动态调试线程池?

面试官表示设置线程池核心线程数是一个非常具有挑战性的事情,问有无办法能够动态的设置线程池核心数,并观察其执行效果?

这个问题的难点在于它涉及到的技术点不是特别常用,该小伙伴面试的技术团队刚好是做运维工具的,做一些监控软件,所以刚好就问到这里。

那么和大家简单聊一聊这个话题。

其实这里主要是涉及到 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 的生命周期。

二 代码实践

接下来松哥通过一个简单的案例,来和大家演示一下如何通过 JMX + jconsole 工具实现动态修改线程池信息。

首先我们先来自定义一个动态线程池:

public class DynamicThreadPool {
private ThreadPoolExecutor threadPoolExecutor;

public DynamicThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> 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 Main {
public static void main(String[] args) throws Exception {
DynamicThreadPool dynamicThreadPool = new DynamicThreadPool(2, 4, 0L, 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 则可以直接修改,修改之后,回到应用程序控制台,我们会发现线程相关数据已经发生变化了。

可以看到,控制台信息已经发生变化。

文章目录
  1. 1. 一 什么是 JMX
  2. 2. 二 代码实践