使用Apache Commons Pool2创建Java对象池

这篇具有很好参考价值的文章主要介绍了使用Apache Commons Pool2创建Java对象池。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

在Java应用程序中,频繁地创建和销毁对象会消耗大量的内存和CPU资源,影响应用程序的性能和可伸缩性。为了解决这个问题,我们可以使用对象池技术,将对象存储在池中,在需要的时候从池中获取,使用完毕后将对象归还到池中。Apache Commons Pool2是一个流行的开源对象池实现,提供了丰富的功能和配置选项,可以满足不同应用程序的需求。

Commons Pool2

Commons Pool2是一个流行的开源对象池实现,提供了丰富的接口和配置选项,使得实现对象池变得非常容易,并且可以根据具体的业务需求进行灵活的配置。Commons Pool2还支持多线程共享对象池中的对象,避免线程之间的竞争,从而提高应用程序的并发性能。

实现一个简单的对象池

下面让我们通过理解和使用Commons Pool2的BasePooledObjectFactory、GenericObjectPool和GenericObjectPoolConfig三个核心接口和类来实现一个对象池。

引入依赖

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

BasePooledObjectFactory

首先,让我们看一下BasePooledObjectFactory类。这个类是一个抽象类,可以用于创建和销毁池中的对象。要使用这个类,我们需要继承它并重写以下方法:
create():用于创建对象,返回一个新的对象。
wrap(T obj):用于包装对象,返回一个PooledObject对象,其中包含了对象本身以及对象状态信息。
validateObject(PooledObject p):用于验证对象是否可用,返回一个布尔值,表示对象是否可用。
destroyObject(PooledObject p):用于销毁对象。

让我们先定义一个对象类,可以在里面编写一些对象的创建、销毁和是否可用等方法,用于示例展示或者测试用途的。

public class MyObject {
    private String name;

    public MyObject(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 MyObjectFactory extends BasePooledObjectFactory<MyObject> {
    @Override
    public MyObject create() throws Exception {
        // 创建一个新的MyObject对象
        MyObject myObject = new MyObject(UUID.randomUUID().toString());
        myObject.create();
        return myObject;
    }

    @Override
    public PooledObject<MyObject> wrap(MyObject myObject) {
        // 将MyObject对象封装到一个PooledObject对象中并返回
        return new DefaultPooledObject<>(myObject);
    }

    @Override
    public void destroyObject(PooledObject<MyObject> pooledObject) throws Exception {
        // 销毁对象
        MyObject myObject = pooledObject.getObject();
        myObject.destroy();
    }

    @Override
    public boolean validateObject(PooledObject<MyObject> pooledObject) {
        // 验证对象是否可用
        MyObject myObject = pooledObject.getObject();
        return myObject.isValid();
    }
}

GenericObjectPoolConfig

接下来,让我们看一下GenericObjectPoolConfig类。这个类是一个配置类,用于配置对象池的属性,例如池大小、最大等待时间、是否允许对象为null等。使用这个类,我们可以根据应用程序的需求来自定义对象池的行为和配置。

下面列出一些配置的默认值和推荐值以及说明。

GenericObjectPoolConfig<MyObject> config = new GenericObjectPoolConfig<>();
config.setMaxTotal(8);          // 对象池中最大对象数
config.setMaxIdle(4);           // 对象池中最大空闲对象数
config.setMinIdle(2);           // 对象池中最小空闲对象数
config.setBlockWhenExhausted(true);  // 当对象池耗尽时,是否等待获取对象
config.setMaxWaitMillis(-1L);   // 对象池没有对象可用时,最大等待时间(单位:毫秒),-1表示无限等待
config.setTestOnCreate(false);  // 创建对象时是否进行对象有效性检查
config.setTestOnBorrow(false);  // 借出对象时是否进行对象有效性检查
config.setTestOnReturn(false);  // 归还对象时是否进行对象有效性检查
config.setTestWhileIdle(false); // 空闲时是否进行对象有效性检查
config.setMinEvictableIdleTimeMillis(1800000L);  // 对象池中对象的最小空闲时间(单位:毫秒)
config.setTimeBetweenEvictionRunsMillis(-1L);    // 后台对象回收器运行的时间间隔(单位:毫秒),-1表示不运行
config.setNumTestsPerEvictionRun(3);  // 后台对象回收器运行时检查对象的个数
config.setSoftMinEvictableIdleTimeMillis(-1L);  // 对象池中对象的最小空闲时间,当空闲对象的数目大于最小空闲数(minIdle)且空闲时间超过此值时,对象将被移除
config.setLifo(true);   // 是否使用后进先出原则借出对象
config.setFairness(false);  // 是否使用公平锁
config.setJmxEnabled(true); // 是否开启 JMX 监控

GenericObjectPool

最后,让我们看一下GenericObjectPool类。这个类是对象池的主要实现,可以用于从池中获取对象、将对象返回到池中以及获取当前池中的对象数量等。在创建对象池时,我们需要提供一个对象工厂类和一个对象池配置类。

使用GenericObjectPool类,我们可以使用以下方法:
borrowObject():用于从对象池中获取对象。如果对象池中没有可用对象,则此方法将阻塞,直到有可用对象为止。
returnObject(T obj):用于将对象返回到对象池中。
getNumIdle():用于获取当前空闲对象的数量。
getNumActive():用于获取当前正在使用的对象的数量。
close():用于关闭对象池。

我这里使用枚举来实现对象池的创建,因为枚举类型是一个线程安全的单例模式,可以避免多线程并发访问时可能出现的竞态条件和同步问题。确保在整个应用程序中只有一个对象池实例存在,可以有效地避免多个对象池实例之间可能出现的冲突和资源浪费问题。

public enum MyObjectPool {
    /**
     * 线程安全的单例
     */
    INSTANCE;

    private GenericObjectPool<MyObject> objectPool;

    MyObjectPool() {
        // 创建对象池配置
        GenericObjectPoolConfig<MyObject> poolConfig = new GenericObjectPoolConfig<>();
        // 对象池中最大对象数
        poolConfig.setMaxTotal(8);
        // 对象池中最小空闲对象数
        poolConfig.setMinIdle(2);
        // 对象池中最大空闲对象数
        poolConfig.setMaxIdle(4);
        // 当对象池耗尽时,是否等待获取对象
        poolConfig.setBlockWhenExhausted(true);
        // 创建对象时是否进行对象有效性检查
        poolConfig.setTestOnCreate(true);
        // 借出对象时是否进行对象有效性检查
        poolConfig.setTestOnBorrow(true);
        // 归还对象时是否进行对象有效性检查
        poolConfig.setTestOnReturn(true);
        // 空闲时是否进行对象有效性检查
        poolConfig.setTestWhileIdle(true);


        // 创建对象工厂
        MyObjectFactory objectFactory = new MyObjectFactory();

        // 创建对象池
        objectPool = new GenericObjectPool<>(objectFactory, poolConfig);
    }

    public MyObject borrowObject() throws Exception {
        // 从对象池中借出一个对象
        return objectPool.borrowObject();
    }

    public void returnObject(MyObject myObject) {
        // 将对象归还给对象池
        objectPool.returnObject(myObject);
    }

    public int getNumActive() throws Exception {
        // 从对象池中借出一个对象
        return objectPool.getNumActive();
    }

    public int getNumIdle() throws Exception {
        // 从对象池中借出一个对象
        return objectPool.getNumIdle();
    }
}

测试

下面我们编写一个简单的测试方法,模拟单线程和多线程两种情况下,对象池管理对象的借出和归还的情况

public class PoolTest {
    public static void main(String[] args) throws Exception {
//        singleTest();
        threadTest();
    }

    public static void singleTest() throws Exception{
        MyObjectPool myObjectPool = MyObjectPool.INSTANCE;


        numActiveAndNumIdle(myObjectPool);
        Thread.sleep(1000);

        MyObject obj = myObjectPool.borrowObject();
        System.out.println("ThreadName:"+ Thread.currentThread().getName() + " borrowed: " + JSONObject.toJSONString(obj));
        Thread.sleep(1000);

        numActiveAndNumIdle(myObjectPool);
        Thread.sleep(1000);

        myObjectPool.returnObject(obj);
        System.out.println("ThreadName:"+ Thread.currentThread().getName() + " returned: " + JSONObject.toJSONString(obj));
        Thread.sleep(1000);

        numActiveAndNumIdle(myObjectPool);

    }

    private static void numActiveAndNumIdle(MyObjectPool myObjectPool) throws Exception{
        int numActive = myObjectPool.getNumActive();
        int numIdle = myObjectPool.getNumIdle();
        System.out.println("ThreadName:"+ Thread.currentThread().getName() + " numActive:" + numActive + " numIdle:" + numIdle);
    }

    public static void threadTest() throws Exception{
        ExecutorService executorService = Executors.newFixedThreadPool(9);

        for (int i = 0; i < 20; i++) {
            executorService.submit(() -> {
                try {
                    singleTest();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }

        executorService.shutdown();
        executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
    }


}

总结

相比于其他实现对象池的技术,使用 Commons Pool2 实现对象池的优点是:它提供了完整的对象池管理功能,包括对象的创建、初始化、借用、归还、清理和销毁等操作,并且支持多线程环境下的并发访问和线程安全。此外,Commons Pool2 还具有灵活的配置选项,可以根据具体场景对对象池的性能和资源消耗进行优化。

缺点是,使用 Commons Pool2 实现对象池需要引入额外的依赖,增加了项目的复杂性。此外,实现和配置对象池需要一定的技术能力,需要了解对象池的原理和相关的配置参数,否则可能会导致对象池的性能和稳定性问题。需要注意的问题包括对象池的配置参数,对象池的线程安全性,对象的有效性检查和对象的回收策略等。文章来源地址https://www.toymoban.com/news/detail-405281.html

到了这里,关于使用Apache Commons Pool2创建Java对象池的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • org.apache.commons.lang3工具类使用

    首先需要引入依赖 常用方法如下:

    2024年02月12日
    浏览(40)
  • java.lang.NoClassDefFoundError: org/apache/commons/io/output/UnsynchronizedByteArrayOutputStream

    一、问题现象 在导出 Excel 过程中,程序报错如下: 二、问题原因 通过报错信息可以看出,这个异常通常出现在你在代码里使用了 org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream 这个类,但是该类所在的依赖包 commons-io 并没有被引入或不存在。 三、解决方案 解决这个问题

    2024年02月10日
    浏览(50)
  • 错误-maven工程,程序包org.apache.commons.xxx不存在,Java:不支持发行版本5

    因为最新的idea界面中文支持较好,就更新了idea,但是发现在导入以前的项目时报了两个错误 程序包org.apache.commons.xxx不存在, Java:不支持发行版本5 那就逐个解决一下 原因就一个,从Java 9开始,以后的编译器无法再生成Java 5二进制文件 Java及其虚拟机高度向后兼容,可以使

    2024年02月01日
    浏览(51)
  • 报错Handler dispatch failed; nested exception is java.lang.NoClassDefFoundError: org/apache/commons/io

    报错: 原因 :这里version 版本未指定,去远程仓库找最新发布版本的构件,可能会冲突 先根据version版本去本地仓库找,如果本地仓库找不到,再判断版本号是否为明确版本号,如果版本号明确,会从远程仓库下载相应版本的依赖 如果版本号不明确,如 RELEASE、LATEST 和 SNAP

    2024年04月12日
    浏览(38)
  • apache commons-dbcp Apache Commons DBCP 软件实现数据库连接池 commons-dbcp2

    许多Apache项目支持与关系型数据库进行交互。为每个用户创建一个新连接可能很耗时(通常需要多秒钟的时钟时间),以执行可能需要毫秒级时间的数据库事务。对于一个公开托管在互联网上的应用程序,在同时在线用户数量可能非常大的情况下,为每个用户打开一个连接可

    2024年03月17日
    浏览(41)
  • Python 线程池 (thread pool) 创建及使用 + 实例代码

    首先线程和线程池不管在哪个语言里面,理论都是通用的。对于开发来说,解决高并发问题离不开对多个线程处理。我们先从线程到线程池,从每个线程的运行到多个线程并行,再到线程池管理。由浅入深的理解如何在实际开发中,使用线程池来提高处理线程的效率。 线程(

    2024年02月05日
    浏览(36)
  • Apache Commons Text 库简介

    简单地说,Apache Commons Text 库包含许多有用的实用程序方法来处理 字符串 ,超出了核心 Java 提供的方法。 在这个快速介绍中,我们将看到Apache Commons Text是什么,它的用途,以及使用库的一些实际示例。 让我们首先将以下 Maven 依赖项添加到我们的 pom.xml : 您可以在Maven 中央

    2024年02月13日
    浏览(45)
  • Mybatis 日志(Apache Commons Logging)

    之前我们介绍了使用JDK Log打印Mybatis运行时的日志;本篇我们介绍使用Apache Commons Logging打印Mybatis运行时的日志。 如何您对Mybatis中使用JDK Log不太了解,可以参考: Mybatis 日志(JDK Log) https://blog.csdn.net/m1729339749/article/details/132565362 在mybatis-config.xml文件中配置logImpl 在配置文件中,

    2024年02月07日
    浏览(39)
  • Apache Commons开源的工具库介绍

            Apache Commons 是 Apache 软件基金会主持的一个项目,旨在提供一系列可重用的 Java 组件。这些组件覆盖了从数据封装、文本处理到网络通信等各个方面,是 Java 开发中常用的一系列工具库。Apache Commons 项目下的各个库通常以 \\\"commons-\\\" 开头命名,例如 Commons Lang、Commo

    2024年02月21日
    浏览(41)
  • 深入理解JVM:Java使用new创建对象的流程

            ①new 对象         ②反射         ③对象的复制         ④反序列化 先看看常量池里面有没有,如果有,就用常量池的 看这个类有没有被加载过,如果没有,就执行类加载以及类的初始化。(对象的大小,在类加载的时候就确定了)。 对象在堆内存

    2024年02月15日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包