前言
大多时候,我们获取对象的方法都是直接new一个。但是,对于大对象的构造,或者构造耗时比较久的对象,我们每次要使用都去new一个是很不科学的。比如数据库的连接对象、redis的连接对象、Http连接请求对象等等。
针对这种场景我们可以创建对象池,这个对象池中维护一定数量的对象,需要的时候就从这个对象池中获取对象,使用完后返还给对象池。这样就避免构造对象所带来的耗时,提升了系统的性能。
为了避免造轮子,我们采用Apache commons-pool对象实现:
org.apache.commons commons-pool2 2.4.2
实现对象池
ObjectPool的主要方法:
//从对象池中获取对象的方法 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抽象工厂模型:
public interface PooledObjectFactory{ //构造一个封装对象 PooledObject makeObject() throws Exception; //销毁对象 void destroyObject(PooledObject p) throws Exception; //验证对象是否可用 boolean validateObject(PooledObject p); //激活一个对象,使其可用用 void activateObject(PooledObject p) throws Exception; //钝化一个对象,也可以理解为反初始化 void passivateObject(PooledObject p) throws Exception;}
以连接池为例
DbConnection对象:
public class DbConnection { private Boolean isActive; public Boolean getActive() { return isActive; } public void setActive(Boolean active) { isActive = active; }}
创建对象工厂DbConnectionFactory:
public class DbConnectionFactory implements PooledObjectFactory{ @Override public PooledObject makeObject() throws Exception { DbConnection dbConnection = new DbConnection(); //构造一个新的连接对象 return new DefaultPooledObject<>(dbConnection); } @Override public void destroyObject(PooledObject p) throws Exception { //断开连接 p.getObject().setActive(false); } @Override public boolean validateObject(PooledObject p) { //判断这个对象是否是保持连接状态 return p.getObject().getActive(); } @Override public void activateObject(PooledObject p) throws Exception { //激活这个对象,让它连接上数据库 p.getObject().setActive(true); } @Override public void passivateObject(PooledObject p) throws Exception { //不处理 }}
测试例子:
public static void main(String[] args) { DbConnectionFactory factory = new DbConnectionFactory(); //设置对象池的相关参数 GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); poolConfig.setMaxIdle(20); poolConfig.setMaxTotal(100); poolConfig.setMinIdle(5); //新建一个对象池,传入对象工厂和配置 GenericObjectPoolobjectPool = 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包含多个字段:
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 类:
public class DbConnectionKeyFactory implements KeyedPooledObjectFactory{ @Override public PooledObject 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 p) throws Exception { p.getObject().setActive(false); } @Override public boolean validateObject(String key, PooledObject p) { return p.getObject().getActive(); } @Override public void activateObject(String key, PooledObject p) throws Exception { p.getObject().setActive(true); } @Override public void passivateObject(String key, PooledObject p) throws Exception { }}
换一个多线程测试:
public static void run() { GenericObjectPoolConfig conf = new GenericObjectPoolConfig(); conf.setMaxTotal(18); conf.setMaxIdle(10); StringPoolFactory factory = new StringPoolFactory(); final GenericObjectPoolobjectPool = 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(); } }
创建的对象一直被复用:
StringPoolFactory对象:
public class StringPoolFactory implements PooledObjectFactory{ public StringPoolFactory() { System.out.println("init string factory.."); } @Override public void activateObject(PooledObject pool) throws Exception { // TODO Auto-generated method stub } @Override public void destroyObject(PooledObject pool) throws Exception { String str = pool.getObject(); if (str != null) { str = null; System.out.println(str + " destroy..."); } } @Override public PooledObject makeObject() throws Exception { String i = UUID.randomUUID().toString(); System.out.println("make " + i + " success..."); return new DefaultPooledObject (i); } @Override public void passivateObject(PooledObject pool) throws Exception { // TODO Auto-generated method stub } @Override public boolean validateObject(PooledObject pool) { // TODO Auto-generated method stub return false; }}
使用GenericKeyedObjectPool 对象:
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(); GenericKeyedObjectPoolgenericKeyedObjectPool = 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包含的参数:
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); }