前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Apache commons-pool对象池妙用

Apache commons-pool对象池妙用

作者头像
春哥大魔王
发布2019-05-05 10:33:19
2K0
发布2019-05-05 10:33:19
举报
文章被收录于专栏:服务端技术杂谈

前言

大多时候,我们获取对象的方法都是直接new一个。但是,对于大对象的构造,或者构造耗时比较久的对象,我们每次要使用都去new一个是很不科学的。比如数据库的连接对象、redis的连接对象、Http连接请求对象等等。

针对这种场景我们可以创建对象池,这个对象池中维护一定数量的对象,需要的时候就从这个对象池中获取对象,使用完后返还给对象池。这样就避免构造对象所带来的耗时,提升了系统的性能。

为了避免造轮子,我们采用Apache commons-pool对象实现:

代码语言:javascript
复制
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.4.2</version>
</dependency>

实现对象池

ObjectPool的主要方法:

代码语言:javascript
复制
    //从对象池中获取对象的方法
    T borrowObject() throws Exception, NoSuchElementException,
            IllegalStateException;
    //将对象返还给对象池
    void returnObject(T obj) throws Exception;
    //让对象失效
    void invalidateObject(T obj) throws Exception;
    //往对象池中新增一个对象
    void addObject() throws Exception, IllegalStateException,
            UnsupportedOperationException;
    //获取当前闲置在对象池中的对象数量,即没有被拿走使用的对象数量
    int getNumIdle();
    //获取已经在使用中的对象数量,即被使用者从对象池中拿走使用的数量
    int getNumActive();
    //清空对象池中闲置的所有对象
    void clear() throws Exception, UnsupportedOperationException;
    //关闭对象池
    void close();

PooledObject,抽象了对象池中对象应该具备的一些属性。注意,这个对象并不是我们真正要存的对象,而是经过一层封装的对象,那么真正存放在对象池的其实都是经过封装过的对象,即PooledObject对象。

PooledObjectFactory抽象工厂模型:

代码语言:javascript
复制
public interface PooledObjectFactory<T> {
  //构造一个封装对象
  PooledObject<T> makeObject() throws Exception;
  //销毁对象
  void destroyObject(PooledObject<T> p) throws Exception;
  //验证对象是否可用
  boolean validateObject(PooledObject<T> p);
  //激活一个对象,使其可用用
  void activateObject(PooledObject<T> p) throws Exception;
   //钝化一个对象,也可以理解为反初始化
  void passivateObject(PooledObject<T> p) throws Exception;
}

以连接池为例

DbConnection对象:

代码语言:javascript
复制
public class DbConnection {

    private Boolean isActive;

    public Boolean getActive() {
        return isActive;
    }

    public void setActive(Boolean active) {
        isActive = active;
    }
}

创建对象工厂DbConnectionFactory:

代码语言:javascript
复制
public class DbConnectionFactory implements PooledObjectFactory<DbConnection> {

    @Override
    public PooledObject<DbConnection> makeObject() throws Exception {
        DbConnection dbConnection = new DbConnection();
        //构造一个新的连接对象
        return new DefaultPooledObject<>(dbConnection);
    }

    @Override
    public void destroyObject(PooledObject<DbConnection> p) throws Exception {
        //断开连接
        p.getObject().setActive(false);
    }

    @Override
    public boolean validateObject(PooledObject<DbConnection> p) {
        //判断这个对象是否是保持连接状态
        return p.getObject().getActive();
    }

    @Override
    public void activateObject(PooledObject<DbConnection> p) throws Exception {
        //激活这个对象,让它连接上数据库
        p.getObject().setActive(true);
    }

    @Override
    public void passivateObject(PooledObject<DbConnection> p) throws Exception {
        //不处理
    }
}

测试例子:

代码语言:javascript
复制
public static void main(String[] args) {
        DbConnectionFactory factory = new DbConnectionFactory();
        //设置对象池的相关参数
        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
        poolConfig.setMaxIdle(20);
        poolConfig.setMaxTotal(100);
        poolConfig.setMinIdle(5);
        //新建一个对象池,传入对象工厂和配置
        GenericObjectPool<DbConnection> objectPool = new GenericObjectPool<>(factory, poolConfig);
        DbConnection dbConnection = null;
        try {
            //从对象池获取对象,如果
            dbConnection = objectPool.borrowObject();
            System.out.println(dbConnection.getActive());
            //使用改对象
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (dbConnection != null) {
                //返还对象
                objectPool.returnObject(dbConnection);
            }
        }
    }

上面的例子中,池化的对象是一样的,如果想池化不一样的对象,可以采用键值对的对象GenericKeyedObjectPool 。

比如新的DbConnection包含多个字段:

代码语言:javascript
复制
public class DbConnection2 {

    private Boolean isActive;
    private String url;

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public Boolean getActive() {
        return isActive;
    }

    public void setActive(Boolean active) {
        isActive = active;
    }
}

实现KeyedPooledObjectFactory 类:

代码语言:javascript
复制
public class DbConnectionKeyFactory implements KeyedPooledObjectFactory<String, DbConnection2> {

    @Override
    public PooledObject<DbConnection2> makeObject(String key) throws Exception {
        DbConnection2 dbConnection2 = new DbConnection2();
        dbConnection2.setUrl(key);
        dbConnection2.setActive(true);
        return new DefaultPooledObject<>(dbConnection2);
    }

    @Override
    public void destroyObject(String key, PooledObject<DbConnection2> p) throws Exception {
        p.getObject().setActive(false);
    }

    @Override
    public boolean validateObject(String key, PooledObject<DbConnection2> p) {
        return p.getObject().getActive();
    }

    @Override
    public void activateObject(String key, PooledObject<DbConnection2> p) throws Exception {
        p.getObject().setActive(true);
    }

    @Override
    public void passivateObject(String key, PooledObject<DbConnection2> p) throws Exception {

    }
}

换一个多线程测试:

代码语言:javascript
复制
    public static void run() {
        GenericObjectPoolConfig conf = new GenericObjectPoolConfig();
        conf.setMaxTotal(18);
        conf.setMaxIdle(10);

        StringPoolFactory factory = new StringPoolFactory();
        final GenericObjectPool<String> objectPool = new GenericObjectPool<>(factory, conf);

        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    while (true) {
                        try {
                            String result = objectPool.borrowObject();
                            System.out.println("result :" + result);
                            Thread.sleep(100);
                            objectPool.returnObject(result);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }

                }
            }).start();

        }
    }

创建的对象一直被复用:

使用GenericKeyedObjectPool 对象:

代码语言:javascript
复制
public static void main(String[] args) {
        GenericKeyedObjectPoolConfig genericKeyedObjectPoolConfig = new GenericKeyedObjectPoolConfig();
        genericKeyedObjectPoolConfig.setMaxIdlePerKey(10);
        genericKeyedObjectPoolConfig.setMaxTotalPerKey(100);
        genericKeyedObjectPoolConfig.setMaxTotal(500);
        genericKeyedObjectPoolConfig.setMinIdlePerKey(10);

        DbConnectionKeyFactory dbConnectionKeyFactory = new DbConnectionKeyFactory();
        GenericKeyedObjectPool<String, DbConnection2> genericKeyedObjectPool = new GenericKeyedObjectPool<>
                (dbConnectionKeyFactory, genericKeyedObjectPoolConfig);
        DbConnection2 dbConnection1 = null;
        DbConnection2 dbConnection2 = null;
        try {
            dbConnection1 = genericKeyedObjectPool.borrowObject("192.168.0.1");
            dbConnection2 = genericKeyedObjectPool.borrowObject("192.168.0.2");
            System.out.println(dbConnection1.getUrl());
            System.out.println(dbConnection2.getUrl());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (dbConnection1 != null) {
                genericKeyedObjectPool.returnObject(dbConnection1.getUrl(), dbConnection1);
            }
            if (dbConnection2 != null) {
                genericKeyedObjectPool.returnObject(dbConnection2.getUrl(), dbConnection2);
            }
        }
    }

Config包含的参数:

代码语言:javascript
复制
	public static void initConfig(GenericObjectPoolConfig cfg){
		
		  	cfg.setLifo( Boolean.valueOf(SysParamsToolkit.getProperty("lifo", "true"))); 
	        cfg.setMaxTotal( Integer.valueOf(SysParamsToolkit.getProperty("maxActive", "18"))); 
	        cfg.setMaxIdle( Integer.valueOf(SysParamsToolkit.getProperty("maxIdle", "10"))); 
	        cfg.setMaxWaitMillis( Integer.valueOf(SysParamsToolkit.getProperty("maxWait", "150000"))); 
	        cfg.setMinEvictableIdleTimeMillis(Integer.valueOf(SysParamsToolkit.getProperty("minEvictableIdleTimeMillis", "100000"))); 
	        cfg.setMinIdle(Integer.valueOf(SysParamsToolkit.getProperty("minIdle", "0"))); 
	        cfg.setNumTestsPerEvictionRun(Integer.valueOf(SysParamsToolkit.getProperty("numTestsPerEvictionRun", "1"))); 
	        cfg.setTestOnBorrow(Boolean.valueOf(SysParamsToolkit.getProperty("testOnBorrow", "false"))); 
	        cfg.setTestOnReturn(Boolean.valueOf(SysParamsToolkit.getProperty("testOnReturn", "false"))); 
	        cfg.setTestWhileIdle(Boolean.valueOf(SysParamsToolkit.getProperty("testWhileIdle", "false"))); 
	        cfg.setTimeBetweenEvictionRunsMillis(Integer.valueOf(SysParamsToolkit.getProperty("timeBetweenEvictionRunsMillis", "120000"))); 
//	        cfg.whenExhaustedAction = Byte.valueOf("whenExhaustedAction", 1); 
	        cfg.setBlockWhenExhausted(false);
	}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-03-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 春哥talk 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 实现对象池
  • 以连接池为例
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档