当前位置: 首页 > article >正文

Apache Commons Pool :介绍与使用

Apache Commons Pool :介绍与使用

什么是 commons-pool2

commons-pool2 是 Apache Commons 提供的一个开源对象池实现框架。它旨在为应用程序提供通用的对象池支持,方便开发者管理资源(如数据库连接、网络连接等)复用,从而减少资源创建与销毁的开销,提高应用程序的性能和可扩展性。


功能特性

  1. 对象池管理:
    • 提供对象的创建、复用、回收、销毁机制。
  2. 多种池实现:
    • 支持通用对象池(GenericObjectPool)和键值对象池(GenericKeyedObjectPool)。
  3. 强大的配置支持:
    • 可以自定义池大小、最大闲置数、最小闲置数等。
  4. 线程安全:
    • 内部实现了高效的并发控制。
  5. 动态调节:
    • 支持在运行时动态调整池的大小和行为。

如何使用

Maven 依赖

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

使用步骤

  1. 创建对象工厂:
    • 实现 org.apache.commons.pool2.PooledObjectFactory 接口,用于定义对象的创建和销毁逻辑。
  2. 初始化对象池:
    • 使用 GenericObjectPoolGenericKeyedObjectPool 来管理对象池。
  3. 获取与归还对象:
    • 使用 borrowObject() 从池中获取对象,用完后通过 returnObject() 将对象归还到池中。

示例:使用 GenericObjectPool

以下是一个简单的数据库连接池实现。

import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.DefaultPooledObject;import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

// 模拟的数据库连接类
class DatabaseConnection {
    private String id;

    public DatabaseConnection(String id) {
        this.id = id;
    }

    public void connect() {
        System.out.println("Connecting to database with ID: " + id);
    }

    public void close() {
        System.out.println("Closing database connection with ID: " + id);
    }

    public String getId() {
        return id;
    }
}

// 数据库连接工厂类
class DatabaseConnectionFactory implements PooledObjectFactory<DatabaseConnection> {
    private int counter = 0;

    @Override
    public PooledObject<DatabaseConnection> makeObject() {
        DatabaseConnection connection = new DatabaseConnection("DB-" + (++counter));
        System.out.println("Creating new connection: " + connection.getId());
        return new DefaultPooledObject<>(connection);
    }

    @Override
    public void destroyObject(PooledObject<DatabaseConnection> p) {
        p.getObject().close();
    }

    @Override
    public boolean validateObject(PooledObject<DatabaseConnection> p) {
        return true; // 假设所有对象都有效
    }

    @Override
    public void activateObject(PooledObject<DatabaseConnection> p) {
        p.getObject().connect();
    }

    @Override
    public void passivateObject(PooledObject<DatabaseConnection> p) {
        // 暂时不需要实现
    }
}

public class ConnectionPoolExample {
    public static void main(String[] args) throws Exception {
        // 配置对象池
        GenericObjectPoolConfig<DatabaseConnection> config = new GenericObjectPoolConfig<>();
        config.setMaxTotal(5);          // 最大对象数
        config.setMaxIdle(3);           // 最大闲置数
        config.setMinIdle(1);           // 最小闲置数
        config.setTestOnBorrow(true);   // 借出时测试对象是否有效

        // 创建对象池
        GenericObjectPool<DatabaseConnection> pool = new GenericObjectPool<>(new DatabaseConnectionFactory(), config);

        // 使用对象池
        DatabaseConnection conn1 = pool.borrowObject(); // 从池中获取对象
        conn1.connect();

        DatabaseConnection conn2 = pool.borrowObject();
        conn2.connect();

        pool.returnObject(conn1); // 将对象归还到池中
        pool.returnObject(conn2);

        // 销毁池
        pool.close();
    }
}

输出示例

Creating new connection: DB-1
Connecting to database with ID: DB-1
Connecting to database with ID: DB-2
Closing database connection with ID: DB-1
Closing database connection with ID: DB-2

优化方式

  1. 合理配置池参数:

    • maxTotal:限制最大对象数,防止过多对象占用内存。
    • minIdle:设置最小闲置数,避免频繁创建与销毁对象。
    • maxWaitMillis:设置获取对象的超时时间。
  2. 定期测试池对象:

    • setTestOnBorrow(true):借出时验证对象。
    • setTestOnReturn(true):归还时验证对象。
    • setTestWhileIdle(true):闲置时验证对象。
  3. 监控池性能:

    • 利用池的 getNumActive()getNumIdle() 方法动态监控池中活动与闲置的对象数。
  4. 资源回收:

    • 确保池在不需要时调用 close() 释放资源。

常见场景

  1. 数据库连接池
  2. 网络连接池
  3. 线程池管理
  4. 文件句柄池

commons-pool2 闲置对象过期及设置详解

默认行为

commons-pool2 的连接池实现中,闲置对象的过期时间以及移除行为由多个参数共同决定。

默认配置

  • 默认情况下,如果没有显式设置移除闲置对象的策略,连接池中的对象可能因为资源限制而被移除。
  • 具体的移除时机依赖于如下参数设置。

相关参数详解

1. minIdle

  • 描述: 最小闲置连接数。
  • 作用: 保证连接池中始终有一定数量的对象处于闲置状态。
  • 默认值: 0(即没有保留最小闲置对象)。
  • 影响: 如果设置为较大的值,连接池会在对象被移除前尝试补充更多的空闲对象。

2. timeBetweenEvictionRunsMillis

  • 描述: 间隔多久运行一次空闲对象的逐出检查线程(以毫秒为单位)。
  • 默认值: -1(表示不运行逐出线程)。
  • 建议优化: 设置为合理的时间间隔,例如 30000 毫秒(30 秒)。

3. minEvictableIdleTimeMillis

  • 描述: 对象在池中保持空闲状态的最小时间(以毫秒为单位),超过该时间的对象可能会被移除。
  • 默认值: 1800000 毫秒(30 分钟)。
  • 建议优化: 设置为较小的值以快速移除闲置对象,例如 600000 毫秒(10 分钟)。

4. softMinEvictableIdleTimeMillis

  • 描述: 对象在池中保持空闲状态的最小时间(以毫秒为单位),但只有当空闲对象超过 minIdle 时才会被移除。
  • 默认值: 未设置。
  • 建议优化: 配合 minIdle 使用,用于延长部分闲置对象的存活时间。

5. numTestsPerEvictionRun

  • 描述: 每次空闲对象检查时检测的对象数量。
  • 默认值: -1(检查所有对象)。
  • 建议优化: 根据池的大小设置为一个合理的值,例如 10

配置示例

以下是一个完整的连接池配置示例:

import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

public class PoolConfigExample {
    public static void main(String[] args) {
        GenericObjectPoolConfig<Object> config = new GenericObjectPoolConfig<>();

        // 设置最小空闲对象数
        config.setMinIdle(5);

        // 设置最大空闲对象数
        config.setMaxIdle(20);

        // 设置最大池容量
        config.setMaxTotal(50);

        // 设置空闲对象逐出线程运行间隔(30 秒)
        config.setTimeBetweenEvictionRunsMillis(30000);

        // 设置空闲对象的最小逐出时间(10 分钟)
        config.setMinEvictableIdleTimeMillis(600000);

        // 检查空闲对象的线程一次处理的数量
        config.setNumTestsPerEvictionRun(5);

        // 创建连接池
        GenericObjectPool<Object> pool = new GenericObjectPool<>(new MyObjectFactory(), config);

        System.out.println("连接池初始化完成!");
    }
}

// 示例对象工厂
class MyObjectFactory extends BasePooledObjectFactory<Object> {
    @Override
    public Object create() throws Exception {
        return new Object(); // 返回一个新对象
    }

    @Override
    public PooledObject<Object> wrap(Object obj) {
        return new DefaultPooledObject<>(obj);
    }
}

总结

空闲对象的移除策略

  • 默认情况下,闲置对象不会自动过期,除非设置了逐出线程参数(如 timeBetweenEvictionRunsMillis)。
  • 设置 minEvictableIdleTimeMillis 可以明确指定闲置对象的过期时间。
  • 通过调优 minIdlemaxIdle 可以更好地控制池中对象的生命周期。

推荐配置

  • 动态调整参数: 根据实际负载和资源需求动态调整 minIdletimeBetweenEvictionRunsMillis
  • 观察性能: 通过性能测试和监控,逐步优化连接池的行为,减少过期对象对系统性能的影响。

Apache Commons Pool: 最小闲置数与逐出线程的交互行为

背景介绍

Apache Commons Pool 是一个高效的对象池管理库,广泛用于连接池、线程池等需要管理资源的场景。其配置包括 最小闲置数 (minIdle)逐出线程 (Evictor) 参数。

  • 最小闲置数 (minIdle)

    • 定义对象池中需要保持的最小空闲对象数。
    • 如果空闲对象少于该值,池会创建新的对象,直到满足 minIdle
  • 逐出线程 (Evictor)

    • 周期性检查并清理空闲对象。
    • timeBetweenEvictionRunsMillisminEvictableIdleTimeMillis 参数控制。

参数交互行为

minIdle 和逐出线程参数同时设置时,逐出线程会考虑 minIdle,确保空闲对象数不低于该值。

逐出线程行为规则

  1. 逐出线程运行规则

    • 定期检查空闲对象是否超过 minEvictableIdleTimeMillis
    • 清理超过空闲时间的对象。
  2. 空闲对象保留规则

    • 如果当前空闲对象数少于或等于 minIdle,逐出线程不会清理这些对象。
    • 若空闲对象数大于 minIdle,逐出线程会清理部分对象,直到空闲对象数等于 minIdle 或更少。

参数对行为的影响

参数描述
minIdle池中最小保留空闲对象数。
timeBetweenEvictionRunsMillis逐出线程运行的时间间隔(毫秒)。
minEvictableIdleTimeMillis对象可被清理的最小空闲时间(毫秒)。

示例代码

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

public class PoolConfigExample {
    public static void main(String[] args) {
        GenericObjectPoolConfig<Object> config = new GenericObjectPoolConfig<>();
        
        // 设置最小空闲数
        config.setMinIdle(5);
        
        // 逐出线程每30秒运行一次
        config.setTimeBetweenEvictionRunsMillis(30000);
        
        // 空闲时间超过60秒的对象可能被清理
        config.setMinEvictableIdleTimeMillis(60000);
        
        // 示例:确保至少保留5个空闲对象
        System.out.println("Pool configured with minIdle=5");
    }
}

特殊场景

  1. 最小闲置数未设置或为0

    • 空闲对象数可能会被清理为 0。
    • 下次获取对象时需要重新创建,可能导致性能下降。
  2. 逐出线程未设置

    • 空闲对象永远不会被清理,可能造成内存占用。

重要结论

  • 逐出线程不会清理到低于 minIdle 的空闲对象数
  • 如果设置合理的 minEvictableIdleTimeMillistimeBetweenEvictionRunsMillis,可以在资源回收与性能间取得平衡。
  • 配置建议:
    • 保证 minIdle 的值与系统负载需求匹配。
    • 根据对象的生命周期合理配置 minEvictableIdleTimeMillis
    • 避免过于频繁或过于稀疏的逐出线程运行时间。

总结

Apache Commons Pool 提供了灵活的参数配置,支持多种资源池管理场景。当 minIdle 和逐出线程参数同时设置时,系统会优先保证 minIdle 的空闲对象数,不会因逐出线程清理而导致空闲对象不足。合理的参数配置可以在资源利用率与性能间找到最佳平衡点。


Apache Commons Pool - 逐出线程设置指南

背景

在使用 Apache Commons Pool 进行对象池管理时,如果未设置逐出线程,空闲对象将永远不会被清理,可能导致内存占用问题。因此,合理配置逐出线程的相关参数可以避免资源泄露,提高系统性能。


关键参数

timeBetweenEvictionRunsMillis

  • 作用:控制逐出线程的运行周期(以毫秒为单位)。
  • 默认值-1,表示逐出线程不运行。
  • 建议设置:例如 60000 表示每分钟运行一次。

minEvictableIdleTimeMillis

  • 作用:设置空闲对象被逐出的最小生存时间(以毫秒为单位)。
  • 默认值1800000(30 分钟)。
  • 建议设置:根据业务需求调整,例如 10 分钟:600000

minIdle

  • 作用:确保池中始终保持的最小空闲对象数。即使逐出线程运行,也不会清理低于该数量的空闲对象。
  • 建议设置:根据系统负载调整,例如 5

maxIdle

  • 作用:限制池中空闲对象的最大数量。如果空闲对象超过此值,多余的对象会被逐出。
  • 建议设置:例如 10

配置代码示例

以下代码展示如何通过 GenericObjectPool 设置逐出线程:

import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

public class PoolExample {
    public static void main(String[] args) {
        // 配置池参数
        GenericObjectPoolConfig<Object> config = new GenericObjectPoolConfig<>();

        // 设置最小空闲连接数
        config.setMinIdle(5);

        // 设置最大空闲连接数
        config.setMaxIdle(10);

        // 设置最大连接数
        config.setMaxTotal(20);

        // 逐出线程运行周期(每 60 秒运行一次)
        config.setTimeBetweenEvictionRunsMillis(60000);

        // 最小可逐出时间(30 分钟未使用的空闲对象会被清理)
        config.setMinEvictableIdleTimeMillis(1800000);

        // 创建对象池
        GenericObjectPool<Object> pool = new GenericObjectPool<>(new ObjectFactory(), config);

        // 使用对象池
        try {
            Object obj = pool.borrowObject(); // 获取对象
            pool.returnObject(obj);           // 归还对象
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            pool.close(); // 关闭池
        }
    }

    // 自定义对象工厂
    static class ObjectFactory extends BasePooledObjectFactory<Object> {
        @Override
        public Object create() {
            return new Object(); // 创建对象
        }

        @Override
        public PooledObject<Object> wrap(Object obj) {
            return new DefaultPooledObject<>(obj); // 包装对象
        }
    }
}

参数详细说明

参数名称作用示例值
setTimeBetweenEvictionRunsMillis控制逐出线程的运行周期。正值表示启用逐出线程,例如 60000 表示每分钟运行一次。60000
setMinEvictableIdleTimeMillis空闲对象的最小生存时间,超过此时间的对象会被逐出。1800000
setMinIdle保证的最小空闲对象数,即使逐出线程运行,也不会清理低于该数量的对象。5
setMaxIdle最大空闲对象数,超过此值的对象会被逐出。10

配置文件示例

可以通过 properties 文件加载配置:

pool.minIdle=5
pool.maxIdle=10
pool.maxTotal=20
pool.timeBetweenEvictionRunsMillis=60000
pool.minEvictableIdleTimeMillis=1800000

在代码中加载配置:

Properties props = new Properties();
try (InputStream input = new FileInputStream("config.properties")) {
    props.load(input);
    GenericObjectPoolConfig<Object> config = new GenericObjectPoolConfig<>();
    config.setMinIdle(Integer.parseInt(props.getProperty("pool.minIdle")));
    config.setMaxIdle(Integer.parseInt(props.getProperty("pool.maxIdle")));
    config.setMaxTotal(Integer.parseInt(props.getProperty("pool.maxTotal")));
    config.setTimeBetweenEvictionRunsMillis(Long.parseLong(props.getProperty("pool.timeBetweenEvictionRunsMillis")));
    config.setMinEvictableIdleTimeMillis(Long.parseLong(props.getProperty("pool.minEvictableIdleTimeMillis")));
}

优化建议

  1. 合理设置最小和最大空闲对象数:避免频繁创建和销毁对象。
  2. 监控池性能:通过池提供的指标(例如活动对象数、空闲对象数)动态调整配置。
  3. 避免逐出线程频繁运行:将逐出线程运行周期设置为合理的值,例如 1 分钟或更长时间。
  4. 测试与调优:根据实际使用场景不断调整参数,平衡性能与资源消耗。

通过合理设置逐出线程和相关参数,可以显著提高对象池的性能和资源利用效率。


线程数量逐渐增加的原因分析与参数设置建议

当线程数量逐渐增加时,可能是由于以下原因或参数设置的问题导致的。以下是详细分析及解决方法。


1. 未限制线程池最大线程数量

  • 原因

    • 未设置线程池的最大线程数 (maxTotal),默认值可能非常大甚至是无界的。随着任务增加,线程池不断创建新线程来满足请求,导致线程数量逐渐增加。
  • 解决方法

    • 设置合理的 maxTotal 值,以限制线程池中的最大线程数。

2. 核心线程数设置过高

  • 原因

    • corePoolSize 是线程池中核心线程的数量。如果设置过高,即使任务较少,线程池也会保持较多的核心线程。
  • 解决方法

    • 根据实际需求,设置一个合理的 corePoolSize 值,以减少空闲线程的数量。

3. 空闲线程未被回收

  • 原因

    • 空闲线程的生存时间参数 (minEvictableIdleTimeMillis) 过长,导致空闲线程无法及时被逐出。
  • 解决方法

    • 设置 minEvictableIdleTimeMillis 为合理的时间(如 30 秒或 60 秒),使得空闲线程能在闲置一段时间后被回收。

4. 逐出线程未启动

  • 原因

    • 未启用逐出线程(Evictor),导致空闲线程不会被定期清理,线程池中的线程数量可能持续累积。
  • 解决方法

    • 设置 timeBetweenEvictionRunsMillis 为一个正值(如 30000 毫秒),开启逐出线程。
    • 配合使用 minEvictableIdleTimeMillisnumTestsPerEvictionRun 参数进行逐出策略优化。

5. 任务积压导致线程池扩容

  • 原因

    • 线程池的队列(workQueue)容量不足时,新任务可能会触发线程池动态扩容。如果任务积压过多,线程池可能创建更多线程来处理任务。
  • 解决方法

    • 使用有限大小的阻塞队列(如 LinkedBlockingQueue),并合理设置其容量。
    • 设置 maximumPoolSize 限制线程数量的扩展。

6. 线程工厂问题

  • 原因

    • 如果线程池使用的 ThreadFactory 未正确管理线程,可能会导致线程池创建更多的线程。
  • 解决方法

    • 确保 ThreadFactory 正确实现,并为线程池创建的线程设置合理的优先级、命名和守护线程属性。

7. 线程泄漏

  • 原因

    • 某些任务未正常退出线程,或线程被错误地持久化,导致线程池中的线程无法被回收。
  • 解决方法

    • 定期监控线程池中的活动线程数。
    • 确保任务逻辑正确,避免线程长期处于阻塞状态。

参数设置示例

以下是一个合理的线程池参数设置,避免线程数量逐渐增加:

GenericObjectPoolConfig<Object> config = new GenericObjectPoolConfig<>();
config.setMaxTotal(100); // 设置线程池最大线程数
config.setMinIdle(10);  // 设置最小闲置线程数
config.setMaxIdle(50);  // 设置最大闲置线程数
config.setTimeBetweenEvictionRunsMillis(30000); // 逐出线程运行间隔
config.setMinEvictableIdleTimeMillis(60000);   // 最小闲置时间
config.setNumTestsPerEvictionRun(3);           // 每次逐出线程检测的对象数

通过合理的参数设置,可以有效控制线程数量,避免不必要的资源消耗。


对象清理行为分析及配置

问题描述

最小闲置数未设置或为0逐出线程未设置 的情况下,如何清理空闲对象?是优先清理最小闲置数为0的对象,还是优先考虑逐出线程未设置的情况,即使最小闲置数为0也不清理?


行为规则分析

1. 最小闲置数未设置或为0

  • 效果
    • 如果 minIdle 设置为 0 或未设置,则空闲池对象可以降到 0 个,即没有任何最小保留的连接或线程。
    • 但仅此设置并不会直接导致清理,因为清理动作需要逐出线程的参与。

2. 逐出线程未设置

  • 效果
    • 如果没有设置逐出线程(即未配置 evictor),则系统不会主动清理空闲对象。
    • 即使 minIdle0,由于缺少逐出线程触发,空闲对象仍然会长期存在。

3. 优先顺序

  • 逐出线程优先控制清理逻辑:如果没有逐出线程,即使 minIdle0,也不会清理空闲对象。
  • 清理动作需要逐出线程定期检查并触发。

总结行为

1. 逐出线程未设置的情况

  • 空闲对象永远不会被清理。
  • 即使 minIdle0,对象也会一直存在,导致资源可能被长期占用。

2. 逐出线程设置了,但 minIdle 为0的情况

  • 逐出线程会按照配置定期运行,清理空闲对象,直到池中对象数量为 0

优化建议

1. 如果希望空闲对象及时释放:

  • 配置逐出线程参数:
    • timeBetweenEvictionRunsMillis:逐出线程运行的时间间隔(单位:毫秒)。
    • minEvictableIdleTimeMillis:空闲对象被逐出的最短存活时间(单位:毫秒)。
  • 设置 minIdle 为合适的非零值(如 minIdle = 2),确保资源池中保留一定数量的空闲对象。

2. 如果不希望清理空闲对象:

  • 设置 minIdle 为大于 0 的值(如 minIdle = 5)。
  • 不设置逐出线程参数,或将 timeBetweenEvictionRunsMillis 设置为一个较大的值,避免频繁触发清理。

示例代码

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

GenericObjectPoolConfig<?> config = new GenericObjectPoolConfig<>();

// 设置最小闲置数为 0
config.setMinIdle(0);

// 配置逐出线程,每 30 秒运行一次
config.setTimeBetweenEvictionRunsMillis(30000);

// 空闲对象超过 60 秒被逐出
config.setMinEvictableIdleTimeMillis(60000);

参数含义

参数名说明示例值
minIdle最小闲置对象数0
timeBetweenEvictionRunsMillis逐出线程运行间隔30000
minEvictableIdleTimeMillis空闲对象存活时间60000

通过合理的配置,可以有效管理资源池对象的生命周期,防止资源浪费或内存泄漏,同时提升系统性能。



http://www.kler.cn/a/460425.html

相关文章:

  • ceph文件系统
  • 彻底解决 Selenium ChromeDriver 不匹配问题:Selenium ChromeDriver 最新版本下载安装教程
  • Spring MVC 的@GetMapping和@PostMapping和@PutMapping
  • python学习笔记—12—
  • C++类对象内存对齐方式
  • logback日志框架源码分析
  • 再见24你好25
  • 计算机网络500题2024-2025学年度第一学期复习题库(选择、判断、填空)
  • C高级:思维导图
  • Kotlin Multiplatform 新纪元:klibs.io 与鸿蒙支持解锁跨平台开发新潜力
  • PTA数据结构作业四
  • 12.31【Linux】shell脚本【运行方式,修改环境变量,数组】思维导图 内附练习
  • Nginx1.20.2-Linux-安装
  • Jenkins 使用入门教程
  • 聊天机器人Rasa面试内容整理-Rasa 是什么?
  • VR动感单车身心调适系统产品特点
  • YK人工智能(三)——万字长文学会torch深度学习
  • vue3中开发一个不定高的虚拟滚动组件
  • SpringBoot + Thymeleaf + Bootstrap5 整合 MyBatis-Plus 和 MySQL 实现动态分类标签渲染教程
  • 郑州时空-TMS运输管理系统 GetDataBase 信息泄露漏洞复现
  • 深信服云桌面系统的终端安全准入设置
  • JavaWeb开发(三)Servlet技术-手动、自动创建Servlet
  • Elasticsearch-搜索推荐:Suggest
  • 记一次 .NET某汗液测试机系统 崩溃分析
  • HTML5 开关(Toggle Switch)详细讲解
  • 如何将联系人从 iPhone 转移到华为 [4 种方法]