简单聊聊对象池及Apache Commons Pool的使用

news/2024/7/24 2:21:34

这里是weihubeats,觉得文章不错可以关注公众号小奏技术,文章首发。拒绝营销号,拒绝标题党

背景

最近在看rocketmq-exporter源码,发现了rocketmq-exporter在管理rocketmq-client使用了一个apache的三方库commons-pool,所以打算研究下commons-pool(对象池化技术)

作用

commons-pool的使用场景是什么呢?很简单,就是一些对象的场景和销毁代价比较大的,就需要使用对象池化技术,也就是commons-pool
类似我们的数据库连接池、线程池

<dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId><version>2.11.1</version>
</dependency>

官网

  • Github: https://github.com/apache/commons-pool
  • 官网文档: https://commons.apache.org/proper/commons-pool/

核心组件

对象工厂

目前对象工厂有两个常用的抽象类

  • BaseKeyedPooledObjectFactory
  • BasePooledObjectFactory

BaseKeyedPooledObjectFactory管理的对象池是一个key value的样子
BasePooledObjectFactory管理的就是一个vlaue对象
举个最简单的例子,比如我们只有一个RocketMQ集群
我们使用BasePooledObjectFactory即可,如果我们有多个RocketMQ集群,不同集群获取的rocketmq-client是不同的,所以我们使用BaseKeyedPooledObjectFactory更合适

ObjectFactory主要是负责对象的创建、初始化、状态检测和消费,这些方法都需要我们自己去实现

这里我们使用BasePooledObjectFactory类给大家演示一个demo
要创建的对象

  • XiaoZou
public class XiaoZou {private final String name;public XiaoZou(String name) {this.name = name;}public void create() {System.out.println("ThreadName:" + Thread.currentThread().getName() + " 对象:" + name + "正在被创建。。。。。。");}public void destroy() {System.out.println("ThreadName:" + Thread.currentThread().getName() + " 对象:" + name + "正在被销毁。。。。。。");}public boolean isValid() {System.out.println("ThreadName:" + Thread.currentThread().getName() + " 对象" + name + "正在检验是否可用。。。。。。");return true;}}

我们的对象工厂

  • XiaoZouBasePooledObjectFactory
public class XiaoZouBasePooledObjectFactory extends BasePooledObjectFactory<XiaoZou> {@Overridepublic XiaoZou create() {// 创建一个新的MyObject对象XiaoZou myObject = new XiaoZou(UUID.randomUUID().toString());myObject.create();return myObject;}@Overridepublic PooledObject<XiaoZou> wrap(XiaoZou myObject) {// 将MyObject对象封装到一个PooledObject对象中并返回return new DefaultPooledObject<>(myObject);}@Overridepublic void destroyObject(PooledObject<XiaoZou> pooledObject) {// 销毁对象XiaoZou myObject = pooledObject.getObject();myObject.destroy();}@Overridepublic boolean validateObject(PooledObject<XiaoZou> pooledObject) {// 验证对象是否可用XiaoZou myObject = pooledObject.getObject();return myObject.isValid();}}

对象池

  • GenericObjectPool
    我们最终操作获取对象都不是通过上面提到的PooledObjectFactory,而是通过GenericObjectPoolGenericObjectPool通过持有PooledObjectFactory然后操作对象

GenericObjectPool实现了ObjectPool接口,ObjectPool定义了操作对象的一些方法

public interface ObjectPool<T> extends Closeable {//添加对象void addObject() throws Exception, IllegalStateException,UnsupportedOperationException;// 批量添加对象default void addObjects(final int count) throws Exception {for (int i = 0; i < count; i++) {addObject();}}//从池中获取对象T borrowObject() throws Exception, NoSuchElementException,IllegalStateException;//清除池,池可用void clear() throws Exception, UnsupportedOperationException;//关闭池,池不可用@Overridevoid close();//获取活跃对象个数int getNumActive();//获取空闲对象个数int getNumIdle();//废弃对象void invalidateObject(T obj) throws Exception;default void invalidateObject(final T obj, final DestroyMode destroyMode) throws Exception {invalidateObject(obj);}//将对象放回池中void returnObject(T obj) throws Exception;}

对象池配置

关于对象池的配置我们都封在在GenericObjectPoolConfig
这里我们可以看看GenericObjectPoolConfig的一个简单配置

// 创建对象池配置
GenericObjectPoolConfig<XiaoZou> poolConfig = new GenericObjectPoolConfig<>();
// 对象池中最大对象数
poolConfig.setMaxTotal(3);
// 对象池中最小空闲对象数
poolConfig.setMinIdle(1);
// 对象池中最大空闲对象数
poolConfig.setMaxIdle(2);
// 当对象池耗尽时,是否等待获取对象
poolConfig.setBlockWhenExhausted(true);
// 创建对象时是否进行对象有效性检查
poolConfig.setTestOnCreate(true);
// 借出对象时是否进行对象有效性检查
poolConfig.setTestOnBorrow(true);
// 归还对象时是否进行对象有效性检查
poolConfig.setTestOnReturn(true);
// 空闲时是否进行对象有效性检查
poolConfig.setTestWhileIdle(true);
// 获取对象最大等待时间 默认 -1 一直等待
poolConfig.setMaxWait(Duration.ofSeconds(2));

其实可以看到和线程池的一些属性有些类似,毕竟都是池

完整demo演示

博客涉及的源码已全部上传至github
地址: https://github.com/weihubeats/weihubeats_demos/tree/master/java-demos/commons-pool-demo

定义一个我们要管理的对象

  • XiaoZou
public class XiaoZou {private final String name;public XiaoZou(String name) {this.name = name;}public void create() {System.out.println("ThreadName:" + Thread.currentThread().getName() + " 对象:" + name + "正在被创建。。。。。。");}public void destroy() {System.out.println("ThreadName:" + Thread.currentThread().getName() + " 对象:" + name + "正在被销毁。。。。。。");}public boolean isValid() {System.out.println("ThreadName:" + Thread.currentThread().getName() + " 对象" + name + "正在检验是否可用。。。。。。");return true;}}
  • 定义对象工厂
public class XiaoZouBasePooledObjectFactory extends BasePooledObjectFactory<XiaoZou> {@Overridepublic XiaoZou create() {// 创建一个新的MyObject对象XiaoZou myObject = new XiaoZou(UUID.randomUUID().toString());myObject.create();return myObject;}@Overridepublic PooledObject<XiaoZou> wrap(XiaoZou myObject) {// 将MyObject对象封装到一个PooledObject对象中并返回return new DefaultPooledObject<>(myObject);}@Overridepublic void destroyObject(PooledObject<XiaoZou> pooledObject) {// 销毁对象XiaoZou myObject = pooledObject.getObject();myObject.destroy();}@Overridepublic boolean validateObject(PooledObject<XiaoZou> pooledObject) {// 验证对象是否可用XiaoZou myObject = pooledObject.getObject();return myObject.isValid();}}
  • 创建对象池
public enum XiaZouPool {/*** 线程安全的单例*/INSTANCE;private final GenericObjectPool<XiaoZou> objectPool;XiaZouPool() {// 创建对象池配置GenericObjectPoolConfig<XiaoZou> poolConfig = new GenericObjectPoolConfig<>();// 对象池中最大对象数poolConfig.setMaxTotal(3);// 对象池中最小空闲对象数poolConfig.setMinIdle(1);// 对象池中最大空闲对象数poolConfig.setMaxIdle(2);// 当对象池耗尽时,是否等待获取对象poolConfig.setBlockWhenExhausted(true);// 创建对象时是否进行对象有效性检查poolConfig.setTestOnCreate(true);// 借出对象时是否进行对象有效性检查poolConfig.setTestOnBorrow(true);// 归还对象时是否进行对象有效性检查poolConfig.setTestOnReturn(true);// 空闲时是否进行对象有效性检查poolConfig.setTestWhileIdle(true);// 获取对象最大等待时间 默认 -1 一直等待poolConfig.setMaxWait(Duration.ofSeconds(2));// 创建对象工厂XiaoZouBasePooledObjectFactory objectFactory = new XiaoZouBasePooledObjectFactory();// 创建对象池objectPool = new GenericObjectPool<>(objectFactory, poolConfig);}/*** 从对象池中借出一个对象* @return* @throws Exception*/public XiaoZou borrowObject() throws Exception {XiaoZou zou = objectPool.borrowObject();int numActive = objectPool.getNumActive();int numIdle = objectPool.getNumIdle();System.out.println("ThreadName:" + Thread.currentThread().getName() + " 活跃对象数量:" + numActive + " 空闲对象数量:" + numIdle);System.out.println("------------------------------------------------------------");return zou;}public void returnObject(XiaoZou myObject) {// 将对象归还给对象池objectPool.returnObject(myObject);}/*** 获取活跃的对象数* @return*/public int getNumActive() {return objectPool.getNumActive();}/*** 获取空闲的对象数* @return*/public int getNumIdle()   {return objectPool.getNumIdle();}}
  • 测试
@Test
public void singleTest() throws Exception{// 最大对象数量3 最小空闲对象数量1 最大空闲数量 2XiaZouPool myObjectPool = XiaZouPool.INSTANCE;numActiveAndNumIdle(myObjectPool);// 获取对象XiaoZou obj = myObjectPool.borrowObject();// 获取对象XiaoZou obj2 = myObjectPool.borrowObject();// 获取对象XiaoZou obj3 = myObjectPool.borrowObject();// 获取对象 已超出最大对象数XiaoZou obj4 = myObjectPool.borrowObject();//归还对象myObjectPool.returnObject(obj);System.out.println("ThreadName:" + Thread.currentThread().getName() + " returned: " + JSONObject.toJSONString(obj));sleep();numActiveAndNumIdle(myObjectPool);
}

这里故意获取对象超过最大对象数量演示超时

总结

本文我们主要学习了Apache Commons Pool是什么,以及核心组件,还有如何使用,不涉及原理和源码的分析。在知道了如何使用后我们后面才会去分析源码


http://wed.xjx100/news/301242.html

相关文章

第六章 敏捷开发与配置管理-作业题-关于Git的题目

小图所在的某校信息学院有一位程老师&#xff0c;他对生命游戏特别感兴趣&#xff0c;正巧他看到小图最近在研究生命游戏。程老师想了一些生命游戏的新规则&#xff0c;他想检验一下那些规则是否有效&#xff0c;于是拍了拍小图的肩膀&#xff0c;语重心长地说&#xff1a;“生…

Java基础重点概要(部分)

为工信部第六届全国计算机信息大赛准备 &#xff0c;主要复习以下内容。 Java基础及环境&#xff1a;JDK发展历史&#xff0c;不同版本的进阶内容。Java程序的编写、编译、调试。 Java程序设计基础&#xff1a;常量和变量的概念&#xff0c;声明方式和作用域。基本数据类型的定…

华为OD机试真题 JavaScript 实现【跳房子II】【2023 B卷 100分】,附详细解题思路

一、题目描述 跳房子&#xff0c;也叫跳飞机&#xff0c;是一种世界性的儿童游戏。 游戏参与者需要分多个回合按顺序跳到第1格直到房子的最后一格&#xff0c;然后获得一次选房子的机会&#xff0c;直到所有房子都被选完&#xff0c;房子最多的人获胜。 跳房子的过程中&…

使用Unity开发一个独立的区块链

Arouse Blockchain [Unity独立区块链] ❗️千万别被误导&#xff0c;上图内容虽然都在项目中可寻&#xff0c;但与目前区块链的业务代码关联不大&#xff0c;仅供宣传作用(总得放些图看着好看)。之所以有以上内容是项目有个目标功能是希望每个用户在区块链上都有一个独一无二的…

表级锁和行级锁

介绍 为什么有事务隔离性&#xff1f;因为要并发执行&#xff0c;数据安全性和一致性和并发效率问题 串行化&#xff1a;锁实现&#xff0c;没有并发性 用的多的是可重复读&#xff08;mysql&#xff09;和已提交读&#xff08;oracle&#xff09;&#xff0c;因为权衡了数据…

5、共享模型之内存

目录 5.1 Java的内存模型5.2 可见性1、退不出的循环2、解决办法&#xff1a;3、可见性 vs 原子性 5.3 有序性1、为什么会有指令重排2、如何禁止指令重排3、原理之volatile4、happens-before 5.1 Java的内存模型 JMM 即 Java Memory Model&#xff08;Java内存模型&#xff09;…

控制并发流程,做好线程间的协调

一、概述 1. 什么是控制并发流程&#xff1f; 线程一般是由线程调度器自动控制的&#xff0c;但有些场景需要按照我们程序员的意愿去实现多线程之间相互配合&#xff0c;从而满足业务逻辑。比如&#xff1a; 让线程A等待线程B执行完后再执行等一些相互合作的逻辑&#xff1b…

MP : Human Motion 人体运动的MLP方法

Back to MLP: A Simple Baseline for Human Motion Prediction conda install -c conda-forge easydict 简介 papercodehttps://arxiv.org/abs/2207.01567v2https://github.com/dulucas/siMLPe Back to MLP是一个仅使用MLP的新baseline,效果SOTA。本文解决了人类运动预测的问…