apache-common pool的使用
Apache commons-pool本质上是"对象池",即通过一定的规则来维护对象集合的容器;commos-pool在很多场景中,用来实现"连接池"/"任务worker池"等,大家常用的dbcp数据库连接池,也是基于commons-pool实现.
commons-pool实现思想非常简单,它主要的作用就是将"对象集合"池化,任何通过pool进行对象存取的操作,都会严格按照"pool配置"(比如池的大小)实时的创建对象/阻塞控制/销毁对象等.它在一定程度上,实现了对象集合的管理以及对象的分发.
1) 将创建对象的方式,使用工厂模式;
2) 通过"pool配置"来约束对象存取的时机
3) 将对象列表保存在队列中(LinkedList)
首选需要声明,不同的"对象池"(或者连接池)在设计上可能存在很大的区别,但是在思想上大同小异,本文主要讲解commons-pool,它和其他"连接池"的区别在此不多讨论.
一.对象生命周期
二.Config详解:
- maxActive: 链接池中最大连接数,默认为8.
- maxIdle: 链接池中最大空闲的连接数,默认为8.
- minIdle: 连接池中最少空闲的连接数,默认为0.
- maxWait: 当连接池资源耗尽时,调用者最大阻塞的时间,超时将跑出异常。单位,毫秒数;默认为-1.表示永不超时.
- minEvictableIdleTimeMillis: 连接空闲的最小时间,达到此值后空闲连接将可能会被移除。负值(-1)表示不移除。
- softMinEvictableIdleTimeMillis: 连接空闲的最小时间,达到此值后空闲链接将会被移除,且保留“minIdle”个空闲连接数。默认为-1.
- numTestsPerEvictionRun: 对于“空闲链接”检测线程而言,每次检测的链接资源的个数。默认为3.
- testOnBorrow: 向调用者输出“链接”资源时,是否检测是有有效,如果无效则从连接池中移除,并尝试获取继续获取。默认为false。建议保持默认值.
- testOnReturn: 向连接池“归还”链接时,是否检测“链接”对象的有效性。默认为false。建议保持默认值.
- testWhileIdle: 向调用者输出“链接”对象时,是否检测它的空闲超时;默认为false。如果“链接”空闲超时,将会被移除。建议保持默认值.
- timeBetweenEvictionRunsMillis: “空闲链接”检测线程,检测的周期,毫秒数。如果为负值,表示不运行“检测线程”。默认为-1.
- 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);
- }
- }
- }
- }
apache-common pool的使用的更多相关文章
- JedisCluster中应用的Apache Commons Pool对象池技术
对象池技术在服务器开发上应用广泛.在各种对象池的实现中,尤其以数据库的连接池最为明显,可以说是每个服务器必须实现的部分. apache common pool 官方文档可以参考:https://c ...
- apache common pool2原理与实战
完整源码,请帮我点个star哦! 原文地址为https://www.cnblogs.com/haixiang/p/14783955.html,转载请注明出处! 简介 对象池顾名思义就是存放对象的池,与 ...
- 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 ...
- NoClassDefFoundError: org/apache/commons/pool/impl/GenericObjectPool
错误:Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/pool/impl ...
- Apache Common DbUtils
前段时间使用了Apache Common DbUtils这个工具,在此留个印,以备不时查看.大家都知道现在市面上的数据库访问层的框架很多,当然很多都是包含了OR-Mapping工作步骤的 例如大家常用 ...
- Apache common pool2 对象池
对象池的容器:包含一个指定数量的对象.从池中取出一个对象时,它就不存在池中,直到它被放回.在池中的对象有生命周期:创建,验证,销毁,对象池有助于更好地管理可用资源,防止JVM内部大量临时小对象,频繁触 ...
- apache common包下的StringUtils的join方法
apache common包下的StringUtils的join方法: 关键字:java string array join public static String join(Iterator it ...
- org.apache.common.io-FileUtils详解
org.apache.common.io---FileUtils详解 getTempDirectoryPath():返回临时目录路径; public static String getTempDire ...
- Spring + Tomcat 启动报错java.lang.ClassNotFoundException: org.apache.commons.pool.impl.GenericObjectPool
错误如下: -- ::,-[TS] INFO http-- org.springframework.beans.factory.support.DefaultListableBeanFactory - ...
- Apache Commons Pool 故事一则
Apache Commons Pool 故事一则 最近工作中遇到一个由于对commons-pool的使用不当而引发的问题,习得正确的使用姿势后,写下这个简单的故事,帮助理解Apache Commons ...
随机推荐
- [Linux]可用于管道操作的命令
管道命令——| command1 | command2 | command3 注:管道命令必须能够接受来自前一个命令的数据成为standard input继续处理. cut 将一段信息的某一段切出来, ...
- android开发之如何使TabHost的TabWidget位于屏幕下方
更改TabHost里的第一个LinearLayout为RelativeLayout.并在TabWidget中添加android:layout_alignParentBottom="true& ...
- ORACLE CUP相关
遭遇cpu过多占用,表现为%usr很高,top 或者topas中cpu占用最多的进程为oracle server process. 则根据pid可以找出该pid对应的sql_text select s ...
- Mysql错误处理
有几种错误处理的声明形式: § 如果任何错误(不是 后继续执行: DECLARE CONTINUE HANDLER FOR SQLEXCEPTION ; § 如果发生任何错误(不是 NOT FOUND ...
- 夺命雷公狗ThinkPHP项目之----企业网站30之网站前台头部导航的高亮显示
我们这个其实也是最简单的一个,首页高亮,那么我们需要先在中间层里面定义一个index = false: 然后在首页控制器里面定义一个 index = true 最后一步就是 在首页的模版上给一个判断: ...
- 管理科学与工程 国内核心期刊 国外a刊及SCI
国内: 管理科学与工程: 管理科学学报 A+ (匿名审稿,绝对牛刊,不比一般的SCi期刊的质量差) 系统工程理论与实践 A (实名审稿,关系稿很多,尤其是挂编委的文章很多,但质量尚可)系统工程 ...
- (第九周)视频发布及git统计报告
项目名:食物链教学工具 组名:奋斗吧兄弟 组长:黄兴 组员:李俞寰.杜桥.栾骄阳.王东涵 代码地址:HTTPS: https://git.coding.net/li_yuhuan/FoodChain. ...
- linux更新系统之后,删除多余的开机启动项
实验环境是centos7,采用uefi的引导方式,启动管理软件是grub2 1. 进入 /boot 目录,应该可以发现许多文件的文件名是以 vmlinuz 开头,后面跟着版本信息,这些就是内核.我们要 ...
- PHP自动生成后台导航网址的最佳方法
'http://www.jbxue.com'=> '脚本学堂首页', </script>
- OpenStack 镜像制作
Contents [hide] 1 Centos6.5 img制作 1.1 基础环境安装 1.2 下载或从本地上传系统镜像 1.3 启动服务 1.4 建立镜像文件 1.5 通过virt-install ...