大多時候,咱們獲取對象的方法都是直接new一個。可是,對於大對象的構造,或者構造耗時比較久的對象,咱們每次要使用都去new一個是很不科學的。好比數據庫的鏈接對象、redis的鏈接對象、Http鏈接請求對象等等。redis
針對這種場景咱們能夠建立對象池,這個對象池中維護必定數量的對象,須要的時候就從這個對象池中獲取對象,使用完後返還給對象池。這樣就避免構造對象所帶來的耗時,提高了系統的性能。數據庫
爲了不造輪子,咱們採用Apache commons-pool對象實現:apache
<!-- 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的主要方法:多線程
//從對象池中獲取對象的方法 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對象。dom
PooledObjectFactory抽象工廠模型:ide
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對象:性能
public class DbConnection { private Boolean isActive; public Boolean getActive() { return isActive; } public void setActive(Boolean active) { isActive = active; } }
建立對象工廠DbConnectionFactory:測試
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 { //不處理 } }
測試例子:this
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 。url
好比新的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<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 { } }
換一個多線程測試:
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(); } }
建立的對象一直被複用:
StringPoolFactory對象:
public class StringPoolFactory implements PooledObjectFactory<String> { public StringPoolFactory() { System.out.println("init string factory.."); } @Override public void activateObject(PooledObject<String> pool) throws Exception { // TODO Auto-generated method stub } @Override public void destroyObject(PooledObject<String> pool) throws Exception { String str = pool.getObject(); if (str != null) { str = null; System.out.println(str + " destroy..."); } } @Override public PooledObject<String> makeObject() throws Exception { String i = UUID.randomUUID().toString(); System.out.println("make " + i + " success..."); return new DefaultPooledObject<String>(i); } @Override public void passivateObject(PooledObject<String> pool) throws Exception { // TODO Auto-generated method stub } @Override public boolean validateObject(PooledObject<String> 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(); 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包含的參數:
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); }