Apache commons-pool對象池妙用

前言

大多時候,咱們獲取對象的方法都是直接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);
	}
相關文章
相關標籤/搜索