apache commons下的pool

其中的borrowObject函数源代码显示其产生可用对象的过程:

如果stack中有空闲的对象,则pop对象,激活对象(activate函数),验证对象(validate函数)。最终将合格的对象返回给client。

若对象在这个流程中出错,则在从stack中取出一个,并执行相同的流程。如此循环,直到stack为空。

如果stack为空,则直接调用makeObject函数创建一个对象。在返回对象之前,还会调用验证函数(validate)验证是否有效。

转载:

Apache common-pool, common-dbcp源码解读与对象池原理剖析 【转载】

博客分类:

最近在做一个内部测试工具类的优化工作中接触到了连接池, 对象池技术, 将原有的未使用连接池的数据库访问操作改成连接池方式.性能有了非常大的提升, 事实证明, 经过两次改造, 原来一个比较大的测试类需要500多秒, 第一次优化后只需要300多秒, 第二次改用连接池之后同一个测试类只需要80多秒.下面是改造过程中的一些总结.
对象池就是以"空间换时间"的一种常用缓存机制, 这里的"时间"特指创建时间,因此这也给出了对象池的适用范围:如果一种对象的创建过程非常耗时的话, 那么请使用对象池. 内部原理简单的说, 就是将创建的对象放到一个容器中, 用完之后不是销毁而是再放回该容器, 让其他的对象调用, 对象池中还涉及到一些高级的技术, 比如过期销毁, 被破坏时销毁, 对象数超过池大小销毁, 对象池中没有可用空闲对象时等待等等.

apache的common-pool工具库是对池化技术原理的一种具体实现. 在阐述原来之前, 这里先理解几个概念: 
对象池(ObjectPool接口): 可以把它认为是一种容器, 它是用来装池对象的, 并且包含了用来创建池对象的工厂对象 
池对象:就是要放到池容器中的对象, 理论上可以是任何对象. 
对象池工厂(ObjectPoolFactory接口):用来创建对象池的工厂, 这个没什么好说的. 
池对象工厂(PoolableObjectFactory接口):用来创建池对象, 将不用的池对象进行钝化(passivateObject), 对要使用的池对象进行激活(activeObject), 对池对象进行验证(validateObject), 对有问题的池对象进行销毁(destroyObject)等工作

对象池中封装了创建, 获取, 归还, 销毁池对象的职责, 当然这些工作都是通过池对象工厂来实施的, 容器内部还有一个或多个用来盛池对象的容器.对象池会对容器大小, 存放时间, 访问等待时间, 空闲时间等等进行一些控制, 因为可以根据需要来调整这些设置.

当需要拿一个池对象的时候, 就从容器中取出一个, 如果容器中没有的话, 而且又没有达到容器的最大限制, 那么就调用池对象工厂, 新建一个池对象, 并调用工厂的激活方法, 对创建的对象进行激活, 验证等一系列操作. 如果已经达到池容器的最大值, 而对象池中又经没有空闲的对象, 那么将会继续等待, 直到有新的空闲的对象被丢进来, 当然这个等待也是有限度的, 如果超出了这个限度, 对象池就会抛出异常.

"出来混, 总是要还的", 池对象也是如此, 当将用完的池对象归还到对象池中的时候, 对象池会调用池对象工厂对该池对象进行验证, 如果验证不通过则被认为是有问题的对象, 将会被销毁, 同样如果容器已经满了, 这个归还池对象将变的"无家可归", 也会被销毁, 如果不属于上面两种情况, 对象池就会调用工厂对象将其钝化并放入容器中. 在整个过程中, 激活, 检查, 钝化处理都不是必须的, 因此我们在实现PoolableObjectFactory接口的时候, 一般不作处理, 给空实现即可, 所以诞生了BasePoolableObjectFactory.

当然你也可以将要已有的对象创建好, 然后通过addObject放到对象池中去, 以备后用.

为了确保对对象池的访问都是线程安全的, 所有对容器的操作都必须放在synchronized中.

在apache的common-pool工具库中有5种对象池:GenericObjectPool和GenericKeyedObjectPool, SoftReferenceObjectPool, StackObjectPool, StackKeyedObjectPool.
五种对象池可分为两类, 一类是无key的: 

另一类是有key的: 

前面两种用CursorableLinkedList来做容器, SoftReferenceObjectPool用ArrayList做容器, 一次性创建所有池化对象, 并对容器中的对象进行了软引用(SoftReference)处理, 从而保证在内存充足的时候池对象不会轻易被jvm垃圾回收, 从而具有很强的缓存能力. 最后两种用Stack做容器. 不带key的对象池是对前面池技术原理的一种简单实现, 带key的相对复杂一些, 它会将池对象按照key来进行分类, 具有相同的key被划分到一组类别中, 因此有多少个key, 就会有多少个容器. 之所以需要带key的这种对象池, 是因为普通的对象池通过makeObject()方法创建的对象基本上都是一模一样的, 因为没法传递参数来对池对象进行定制. 因此四种池对象的区别主要体现在内部的容器的区别, Stack遵循"后进先出"的原则并能保证线程安全, CursorableLinkedList是一个内部用游标(cursor)来定位当前元素的双向链表, 是非线程安全的, 但是能满足对容器的并发修改.ArrayList是非线程安全的, 便利方便的容器.

使用对象池的一般步骤:创建一个池对象工厂, 将该工厂注入到对象池中, 当要取池对象, 调用borrowObject, 当要归还池对象时, 调用returnObject, 销毁池对象调用clear(), 如果要连池对象工厂也一起销毁, 则调用close().
下面是一些时序图: 
borrowObject: 

returnObject: 

invalidateObject: 

apache的连接池工具库common-dbcp是common-pool在数据库访问方面的一个具体应用.当对common-pool熟悉之后, 对common-dbcp就很好理解了. 它通过对已有的Connection, Statment对象包装成池对象PoolableConnection, PoolablePreparedStatement. 然后在这些池化的对象中, 持有一个对对象池的引用, 在关闭的时候, 不进行真正的关闭处理, 而是通过调用:

  1. _pool.returnObject(this);
  2. _pool.returnObject(_key,this);

这样一句, 将连接对象放回连接池中. 
而对应的对象池前者采用的是ObjectPool, 后者是KeyedObjectPool, 因为一个数据库只对应一个连接, 而执行操作的Statement却根据Sql的不同会分很多种. 因此需要根据sql语句的不同多次进行缓存
在对连接池的管理上, common-dbcp主要采用两种对象: 
一个是PoolingDriver, 另一个是PoolingDataSource, 二者的区别是PoolingDriver是一个更底层的操作类, 它持有一个连接池映射列表, 一般针对在一个jvm中要连接多个数据库, 而后者相对简单一些. 内部只能持有一个连接池, 即一个数据源对应一个连接池.
下面是common-dbcp的结构关系: 

下面是参考了common-dbcp的例子之后写的一个从连接池中获取连接的工具类

  1. /**
  2. * 创建连接
  3. *
  4. * @since 2009-1-22 下午02:58:35
  5. */
  6. public class ConnectionUtils {
  7. // 一些common-dbcp内部定义的protocol
  8. private static final String POOL_DRIVER_KEY = "jdbc:apache:commons:dbcp:";
  9. private static final String POLLING_DRIVER = "org.apache.commons.dbcp.PoolingDriver";
  10. /**
  11. * 取得池化驱动器
  12. *
  13. * @return
  14. * @throws ClassNotFoundException
  15. * @throws SQLException
  16. */
  17. private static PoolingDriver getPoolDriver() throws ClassNotFoundException,
  18. SQLException {
  19. Class.forName(POLLING_DRIVER);
  20. return (PoolingDriver) DriverManager.getDriver(POOL_DRIVER_KEY);
  21. }
  22. /**
  23. * 销毁所有连接
  24. *
  25. * @throws Exception
  26. */
  27. public static void destory() throws Exception {
  28. PoolingDriver driver = getPoolDriver();
  29. String[] names = driver.getPoolNames();
  30. for (String name : names) {
  31. driver.getConnectionPool(name).close();
  32. }
  33. }
  34. /**
  35. * 从连接池中获取数据库连接
  36. */
  37. public static Connection getConnection(TableMetaData table)
  38. throws Exception {
  39. String key = table.getConnectionKey();
  40. PoolingDriver driver = getPoolDriver();
  41. ObjectPool pool = null;
  42. // 这里找不到连接池会抛异常, 需要catch一下
  43. try {
  44. pool = driver.getConnectionPool(key);
  45. } catch (Exception e) {
  46. }
  47. if (pool == null) {
  48. // 根据数据库类型构建连接工厂
  49. ConnectionFactory connectionFactory = null;
  50. if (table.getDbAddr() != null
  51. && TableMetaData.DB_TYPE_MYSQL == table.getDbType()) {
  52. Class.forName(TableMetaData.MYSQL_DRIVER);
  53. connectionFactory = new DriverManagerConnectionFactory(table
  54. .getDBUrl(), null);
  55. } else {
  56. Class.forName(TableMetaData.ORACLE_DRIVER);
  57. connectionFactory = new DriverManagerConnectionFactory(table
  58. .getDBUrl(), table.getDbuser(), table.getDbpass());
  59. }
  60. // 构造连接池
  61. ObjectPool connectionPool = new GenericObjectPool(null);
  62. new PoolableConnectionFactory(connectionFactory, connectionPool,
  63. null, null, false, true);
  64. // 将连接池注册到driver中
  65. driver.registerPool(key, connectionPool);
  66. }
  67. // 从连接池中拿一个连接
  68. return DriverManager.getConnection(POOL_DRIVER_KEY + key);
  69. }
  70. }

虽然对象池技术在实际开发过程中用的不是很多, 但是理解之后对我们写程序还是有莫大的好处的, 至少我是这样的

apache commons pool的更多相关文章

  1. Tomcat 开发web项目报Illegal access: this web application instance has been stopped already. Could not load [org.apache.commons.pool.impl.CursorableLinkedList$Cursor]. 错误

    开发Java web项目,在tomcat运行后报如下错误: Illegal access: this web application instance has been stopped already ...

  2. NoClassDefFoundError: org/apache/commons/pool/impl/GenericObjectPool

    错误:Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/pool/impl ...

  3. Spring + Tomcat 启动报错java.lang.ClassNotFoundException: org.apache.commons.pool.impl.GenericObjectPool

    错误如下: -- ::,-[TS] INFO http-- org.springframework.beans.factory.support.DefaultListableBeanFactory - ...

  4. Apache Commons Pool 故事一则

    Apache Commons Pool 故事一则 最近工作中遇到一个由于对commons-pool的使用不当而引发的问题,习得正确的使用姿势后,写下这个简单的故事,帮助理解Apache Commons ...

  5. 池化 - Apache Commons Pool

    对于那些创建耗时较长,或者资源占用较多的对象,比如网络连接,线程之类的资源,通常使用池化来管理这些对象,从而达到提高性能的目的.比如数据库连接池(c3p0, dbcp), java的线程池 Execu ...

  6. org/apache/commons/pool/impl/GenericObjectPool异常的解决办法

    org/apache/commons/pool/impl/GenericObjectPool异常的解决办法 webwork+spring+hibernate框架的集成, 一启动Tomcat服务器就出了 ...

  7. 对象池化技术 org.apache.commons.pool

    恰当地使用对象池化技术,可以有效地减少对象生成和初始化时的消耗,提高系统的运行效率.Jakarta Commons Pool组件提供了一整套用于实现对象池化的框架,以及若干种各具特色的对象池实现,可以 ...

  8. JedisCluster中应用的Apache Commons Pool对象池技术

    对象池技术在服务器开发上应用广泛.在各种对象池的实现中,尤其以数据库的连接池最为明显,可以说是每个服务器必须实现的部分.   apache common pool 官方文档可以参考:https://c ...

  9. Cache Lucene IndexReader with Apache Commons Pool

    IndexReaderFactory.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 2 ...

  10. Java_异常_03_ java.lang.NoClassDefFoundError: org/apache/commons/pool/KeyedObjectPoolFactory

    异常信息: java.lang.NoClassDefFoundError: org/apache/commons/pool/KeyedObjectPoolFactory 原因: 我用的是commons ...

随机推荐

  1. Microsoft.Crm.Setup.SrsDataConector.RegisterServerAction 操作失败 Requested value 'Geo' was not found 的解决方法

    error installing ssrs data connector on sql server for dynamics crm 2011 I think the post title says ...

  2. C# Request.Params与Request.QueryString 的区别

    1.Request.Params包含Request.QueryString,request.form.request.cookies和request.servervariables.这几种查找的时候会 ...

  3. bzoj 4911: [Sdoi2017]切树游戏

    考虑维护原树的lct,在上面dp,由于dp方程特殊,均为异或卷积或加法,计算中可以只使用fwt后的序列 v[w]表示联通子树的最浅点为w,且不选w的splay子树中的点 l[w]表示联通子树的最浅点在 ...

  4. Spring Cloud构建微服务架构(七)消息总线

    先回顾一下,在之前的Spring Cloud Config的介绍中,我们还留了一个悬念:如何实现对配置信息的实时更新.虽然,我们已经能够通过/refresh接口和Git仓库的Web Hook来实现Gi ...

  5. Jenkins git鬼畜集

    1. Jnekins服务重启后,莫名奇妙就报403错误,内网OK,把内网IP换成外网域名又可以. 莫名其妙解决:点了下notfiy后的Add,然后重输了一次密码,好了....但是这是新增一个账号啊!! ...

  6. ssh的应用和vnc连接桌面

    什么是ssh? SSH是一种网络协议,用于计算机之间的加密登录.如果一个用户从本地计算机,使用SSH协议登录另一台远程计算机,我们就可以认为,这种登录是安全的,即使被中途截获,密码也不会泄露.SSH之 ...

  7. java UTC时间和local时间相互转换

    java UTC时间和local时间相互转换 1.local时间转UTC时间 /** * local时间转换成UTC时间 * @param localTime * @return */ public ...

  8. mysql 字符集排查

    mysql 字符集排查 库级别 SELECT * FROM information_schema.schemata WHERE schema_name NOT IN ( 'information_sc ...

  9. numpy的shuffle函数

    import numpy as np from numpy.random import shuffle import pandas as pd df = pd.DataFrame([[1,2,3],[ ...

  10. 信息学奥赛(NOIP)复赛学习方法推荐

    一.确定你的语言 NOIP包括三种语言c/c++/pascal,在最初必须确定自己使用的语言.没有c/c++基础的,个人建议使用pascal,因为它更容易上手,如果有充裕的时间,则建议c/c++,因为 ...