大多时候,我们获取对象的方法都是直接new一个。但是,对于大对象的构造,或者构造耗时比较久的对象,我们每次要使用都去new一个是很不科学的。比如数据库的连接对象、redis的连接对象、Http连接请求对象等等。
针对这种场景我们可以创建对象池,这个对象池中维护一定数量的对象,需要的时候就从这个对象池中获取对象,使用完后返还给对象池。这样就避免构造对象所带来的耗时,提升了系统的性能。
为了避免造轮子,我们采用Apache commons-pool对象实现:
<!-- 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对象。
PooledObjectFactory抽象工厂模型:
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 {
//不处理
}
}
测试例子:
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包含多个字段:
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();
}
}
创建的对象一直被复用:
使用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);
}