Apache commons-pool对象池原理分析 小灰灰 2021-05-12 11:54 409阅读 0赞 ** Apache commons-pool**本质上是"对象池",即通过一定的规则来维护对象集合的容器;commos-pool在很多场景中,用来实现"连接池"/"任务worker池"等,大家常用的dbcp数据库连接池,也是基于commons-pool实现. commons-pool实现思想非常简单,它主要的作用就是将"对象集合"池化,任何通过pool进行对象存取的操作,都会严格按照"pool配置"(比如池的大小)实时的创建对象/阻塞控制/销毁对象等.它在一定程度上,实现了对象集合的管理以及对象的分发. 1) 将创建对象的方式,使用工厂模式; 2) 通过"pool配置"来约束对象存取的时机 3) 将对象列表保存在队列中(LinkedList) 首选需要声明,不同的"对象池"(或者连接池)在设计上可能存在很大的区别,但是在思想上大同小异,本文主要讲解commons-pool,它和其他"连接池"的区别在此不多讨论. **一.对象生命周期:** ![Center][] **二.Config详解:** 1. maxActive: 链接池中最大连接数,默认为8. 2. maxIdle: 链接池中最大空闲的连接数,默认为8. 3. minIdle: 连接池中最少空闲的连接数,默认为0. 4. maxWait: 当连接池资源耗尽时,调用者最大阻塞的时间,超时将跑出异常。单位,毫秒数;默认为-1.表示永不超时. 5. minEvictableIdleTimeMillis: 连接空闲的最小时间,达到此值后空闲连接将可能会被移除。负值(-1)表示不移除。 6. softMinEvictableIdleTimeMillis: 连接空闲的最小时间,达到此值后空闲链接将会被移除,且保留“minIdle”个空闲连接数。默认为-1. 7. numTestsPerEvictionRun: 对于“空闲链接”检测线程而言,每次检测的链接资源的个数。默认为3. 8. testOnBorrow: 向调用者输出“链接”资源时,是否检测是有有效,如果无效则从连接池中移除,并尝试获取继续获取。默认为false。建议保持默认值. 9. testOnReturn: 向连接池“归还”链接时,是否检测“链接”对象的有效性。默认为false。建议保持默认值. 10. testWhileIdle: 向调用者输出“链接”对象时,是否检测它的空闲超时;默认为false。如果“链接”空闲超时,将会被移除。建议保持默认值. 11. timeBetweenEvictionRunsMillis: “空闲链接”检测线程,检测的周期,毫秒数。如果为负值,表示不运行“检测线程”。默认为-1. 12. whenExhaustedAction: 当“连接池”中active数量达到阀值时,即“链接”资源耗尽时,连接池需要采取的手段, 默认为1: -> 0 : 抛出异常, -> 1 : 阻塞,直到有可用链接资源 -> 2 : 强制创建新的链接资源 这些属性均可以在org.apache.commons.pool.impl.GenericObjectPool.Config中进行设定。 **三.原理解析** ***1) 对象池创建(参考GenericObjectPool):*** * *public GenericObjectPool(PoolableObjectFactory factory, GenericObjectPool.Config config)* :此方法创建一个GenericObjectPool实例,GenericObjectPool类已经实现了和对象池有关的所有核心操作,开发者可以通过继承或者封装的方式来使用它.通过此构造函数,我们能够清晰的看到,一个Pool中需要指定PoolableObjectFactory 实例,以及此对象池的Config信息.PoolableObjectFactory主要用来"创建新对象",比如当对象池中的对象不足时,可以使用PoolableObjectFactory.makeObject()方法来创建对象,并交付给Pool管理. 此构造函数实例化了一个*LinkedList作为"对象池"容器*,用来存取"对象".此外还会根据timeBetweenEvictionRunsMillis的值来决定是否启动一个后台线程,此线程用来周期性扫描pool中的对象列表,已检测"对象池中的对象"空闲(idle)的时间是否达到了阀值,如果是,则移除此对象. if ((getMinEvictableIdleTimeMillis() > 0) && (idleTimeMilis > getMinEvictableIdleTimeMillis())) { removeObject = true; } ... if (removeObject) { try { _factory.destroyObject(pair.value); } catch(Exception e) { // ignored } } ***2) 对象工厂PoolableObjectFactory接口:*** commons-pool通过使用ObjectFactory(工厂模式)的方式将"对象池中的对象"的创建/检测/销毁等特性解耦出来,这是一个非常良好的设计思想.此接口有一个抽象类BasePoolableObjectFactory,可供开发者继承和实现. * *Object makeObject()* : 创建一个新对象;当对象池中的对象个数不足时,将会使用此方法来"输出"一个新的"对象",并交付给对象池管理. * *void destroyObject(Object obj)* : 销毁对象,如果对象池中检测到某个"对象"idle的时间超时,或者操作者向对象池"归还对象"时检测到"对象"已经无效,那么此时将会导致"对象销毁";"销毁对象"的操作设计相差甚远,但是必须明确:当调用此方法时,"对象"的生命周期必须结束.如果object是线程,那么此时线程必须退出;如果object是socket操作,那么此时socket必须关闭;如果object是文件流操作,那么此时"数据flush"且正常关闭. * *boolean validateObject(Object obj)* : 检测对象是否"有效";Pool中不能保存无效的"对象",因此"后台检测线程"会周期性的检测Pool中"对象"的有效性,如果对象无效则会导致此对象从Pool中移除,并destroy;此外在调用者从Pool获取一个"对象"时,也会检测"对象"的有效性,确保不能讲"无效"的对象输出给调用者;当调用者使用完毕将"对象归还"到Pool时,仍然会检测对象的有效性.所谓有效性,就是此"对象"的状态是否符合预期,是否可以对调用者直接使用;如果对象是Socket,那么它的有效性就是socket的通道是否畅通/阻塞是否超时等. * *void activateObject(Object obj)* : "激活"对象,当Pool中决定移除一个对象交付给调用者时额外的"激活"操作,比如可以在activateObject方法中"重置"参数列表让调用者使用时感觉像一个"新创建"的对象一样;如果object是一个线程,可以在"激活"操作中重置"线程中断标记",或者让线程从阻塞中唤醒等;如果object是一个socket,那么可以在"激活操作"中刷新通道,或者对socket进行链接重建(假如socket意外关闭)等. * *void void passivateObject(Object obj)* : "钝化"对象,当调用者"归还对象"时,Pool将会"钝化对象";钝化的言外之意,就是此"对象"暂且需要"休息"一下.如果object是一个socket,那么可以passivateObject中清除buffer,将socket阻塞;如果object是一个线程,可以在"钝化"操作中将线程sleep或者将线程中的某个对象wait.需要注意的时,activateObject和passivateObject两个方法需要对应,避免死锁或者"对象"状态的混乱. *** 3) ObjectPool接口与实现:*** 对象池是commons-pool的核心接口,用来维护"对象列表"的存取;其中GenericObjectPool是其实现类,它已经实现了相关的功能. * *Object borrowObject()* : 从Pool获取一个对象,此操作将导致一个"对象"从Pool移除(脱离Pool管理),调用者可以在获得"对象"引用后即可使用,且需要在使用结束后"归还".如下为伪代码: public Object borrowObject() throws Exception { Object value = null; synchronized (this) { if(!_pool.isEmpty()){ value = _pool.remove(); } } for(;;) { //如果Pool中没有"对象",则根据相应的"耗尽"策略 if(value == null) { switch(whenExhaustedAction) { //如果耗尽,仍继续创建新"对象" case WHEN_EXHAUSTED_GROW: value = _factory.makeObject(); break; //如果耗尽,则终止,此时以异常的方式退出. case WHEN_EXHAUSTED_FAIL: throw new NoSuchElementException("Pool exhausted"); //如果耗尽,则阻塞,直到有"对象"归还 case WHEN_EXHAUSTED_BLOCK: try { synchronized (value) { if (value == null) { //maxWait为Config中指定的"最大等待时间" if(maxWait <= 0) { latch.wait(); } else { latch.wait(waitTime); } } else { break; } } } catch(InterruptedException e) { // break; } default:// } } try { _factory.activateObject(latch.getPair().value); if(_testOnBorrow && !_factory.validateObject(latch.getPair().value)) { throw new Exception("ValidateObject failed"); } return value; } catch (Throwable e) { try { _factory.destroyObject(latch.getPair().value); } catch (Throwable e2) { // } } } } * *void returnObject(Object obj) : *"归还"对象,当"对象"使用结束后,需要归还到Pool中,才能维持Pool中对象的数量可控,如果不归还到Pool,那么将意味着在Pool之外,将有大量的"对象"存在,那么就使用了"对象池"的意义.如下为伪代码: public void returnObject(Object obj) throws Exception { try { boolean success = true;// if(_testOnReturn && !(_factory.validateObject(obj))) { success = false; } else { _factory.passivateObject(obj); } synchronized (this) { //检测pool中已经空闲的对象个数是否达到阀值. if((_maxIdle >= 0) && (_pool.size() >= _maxIdle)) { success = false; } else if(success) { _pool.addFirst(new ObjectTimestampPair(obj)); } } // Destroy the instance if necessary if(!success) { try { _factory.destroyObject(obj); } catch(Exception e) { // ignored } } } catch (Exception e) { // } } * *void invalidateObject(Object obj)* : 销毁对象,直接调用ObjectFactory.destroyObject(obj);. * *void addObject()* : 开发者可以直接调用addObject方法用于直接创建一个"对象"并添加到Pool中. **四.代码实例.** 本实例主要用来演示一个"TCP连接池". ***1) ConnectionPoolFactory.java:*** import org.apache.commons.pool.BasePoolableObjectFactory; import org.apache.commons.pool.impl.GenericObjectPool; import org.apache.commons.pool.impl.GenericObjectPool.Config; public class ConnectionPoolFactory { private GenericObjectPool pool; public ConnectionPoolFactory(Config config,String ip,int port){ ConnectionFactory factory = new ConnectionFactory(ip, port); pool = new GenericObjectPool(factory, config); } public Socket getConnection() throws Exception{ return (Socket)pool.borrowObject(); } public void releaseConnection(Socket socket){ try{ pool.returnObject(socket); }catch(Exception e){ if(socket != null){ try{ socket.close(); }catch(Exception ex){ // } } } } /** * inner * @author qing * */ class ConnectionFactory extends BasePoolableObjectFactory { private InetSocketAddress address; public ConnectionFactory(String ip,int port){ address = new InetSocketAddress(ip, port); } @Override public Object makeObject() throws Exception { Socket socket = new Socket(); socket.connect(address); return socket; } public void destroyObject(Object obj) throws Exception { if(obj instanceof Socket){ ((Socket)obj).close(); } } public boolean validateObject(Object obj) { if(obj instanceof Socket){ Socket socket = ((Socket)obj); if(!socket.isConnected()){ return false; } if(socket.isClosed()){ return false; } return true; } return false; } } } *2) TestMain.java(测试类):* public class TestMain { /** * @param args */ public static void main(String[] args) { Config config = new Config(); config.maxActive = 16; config.maxWait = 30000; ConnectionPoolFactory poolFactory = new ConnectionPoolFactory(config, "127.0.0.1", 8011); Socket socket = null ; try{ socket = poolFactory.getConnection(); }catch(Exception e){ e.printStackTrace(); }finally{ if(socket != null){ poolFactory.releaseConnection(socket); } } } } [Center]: /images/20210511/7e84572d186b4638937e77ccd821e669.png
相关 Java对象池技术:原理与实现案例分析 Java对象池技术是一种资源管理方式,用于重复利用已创建的Java对象。它的主要优点包括减少内存碎片、提高系统响应速度等。 一、原理: 1. **预创建**:对象池会预先创建 你的名字/ 2024年10月19日 03:57/ 0 赞/ 58 阅读
相关 性能瓶颈分析:Java对象池原理与实践 Java对象池是一种常用的资源管理策略,主要用于复用已经创建的对象以减少新创建对象的开销。以下是对Java对象池原理及实践的一些理解: 1. 原理: - 对象预创建:预 爱被打了一巴掌/ 2024年09月10日 17:48/ 0 赞/ 75 阅读
相关 Apache commons-pool对象池原理分析 Apache commons-pool本质上是"对象池",即通过一定的规则来维护对象集合的容器;commos-pool在很多场景中,用来实现"连接池"/"任务worker池 客官°小女子只卖身不卖艺/ 2022年06月13日 03:08/ 0 赞/ 215 阅读
相关 unity对象池技术原理杂谈 首先我们来谈一谈对象池技术的介绍:由于Cpu回收游戏对象和创建游戏对象,涉及到内存的分配和回收,研究过操作的同学应该知道当应用程序也就是我们的游戏需要生产一个怪物的时候,我们首 亦凉/ 2022年06月06日 03:30/ 0 赞/ 274 阅读
相关 Java 线程池原理分析 1.简介 线程池可以简单看做是一组线程的集合,通过使用线程池,我们可以方便的复用线程,避免了频繁创建和销毁线程所带来的开销。在应用上,线程池可应用在后端相关服务中。比如 墨蓝/ 2022年05月27日 12:25/ 0 赞/ 251 阅读
相关 apache-common-pool2对象池的使用 一、概述 大多时候,我们获取对象的方法都是直接new一个。但是,对于大对象的构造,或者构造耗时比较久的对象,我们每次要使用都去new一个是很不科学的。比如数据库的连接对象 待我称王封你为后i/ 2022年05月20日 06:56/ 0 赞/ 288 阅读
相关 线程池流程分析原理 > 线程池流程分析原理 1)首先,要清楚corePoolSize和maximumPoolSize的含义; 2)其次,要知道Worker是用来起到什么作 旧城等待,/ 2022年03月16日 08:52/ 0 赞/ 243 阅读
相关 org.apache.commons.pool 对象池 一、背景介绍 创建新的对象并初始化的操作,可能会消耗很多的时间。在需要频繁创建并使用这些对象的场景中,为了提供系统性能,通常的做法是,创建一个对象池,将一定数量的对象缓存 比眉伴天荒/ 2021年11月09日 06:44/ 0 赞/ 232 阅读
相关 Apache commons-pool对象池原理分析 Apache commons-pool本质上是"对象池",即通过一定的规则来维护对象集合的容器;commos-pool在很多场景中,用来实现"连接池"/"任务worker池" 小灰灰/ 2021年05月12日 11:54/ 0 赞/ 410 阅读
还没有评论,来说两句吧...