如何动态调试线程池?

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

如何动态调试线程池?

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

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

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

其实这里主要是涉及到 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 则可以直接修改,修改之后,回到应用程序控制台,我们会发现线程相关数据已经发生变化了。

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

优秀的个人博客,低调大师

微信关注我们

原文链接:https://my.oschina.net/lenve/blog/16666291

转载内容版权归作者及来源网站所有!

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

相关文章

发表评论

资源下载

更多资源
优质分享Android(本站安卓app)

优质分享Android(本站安卓app)

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Oracle Database,又名Oracle RDBMS

Oracle Database,又名Oracle RDBMS

Oracle Database,又名Oracle RDBMS,或简称Oracle。是甲骨文公司的一款关系数据库管理系统。它是在数据库领域一直处于领先地位的产品。可以说Oracle数据库系统是目前世界上流行的关系数据库管理系统,系统可移植性好、使用方便、功能强,适用于各类大、中、小、微机环境。它是一种高效率、可靠性好的、适应高吞吐量的数据库方案。

Eclipse(集成开发环境)

Eclipse(集成开发环境)

Eclipse 是一个开放源代码的、基于Java的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。幸运的是,Eclipse 附带了一个标准的插件集,包括Java开发工具(Java Development Kit,JDK)。

Java Development Kit(Java开发工具)

Java Development Kit(Java开发工具)

JDK是 Java 语言的软件开发工具包,主要用于移动设备、嵌入式设备上的java应用程序。JDK是整个java开发的核心,它包含了JAVA的运行环境(JVM+Java系统类库)和JAVA工具。