为什么要用对象池
解决大对象的创建和销毁时的资源消耗。所以,常见的对象池有数据库连接池、线程池等
Apache Commons-pool2几个重要对象
- ObjectPool<T>
实现对对象存取和状态管理的池实现;如:线程池、数据库连接池
public interface ObjectPool<T> {
//从池子中获取一个对象
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<T> extends Comparable<PooledObject<T>>
被池化的对象的包装类,在原对象的基础上添加了一些附加的信息,比如说状态信息,创建时间,激活时间,关闭时间等 - PooledObjectFactory<T>
被池化对象的工厂类,用于创建对象、销毁对象、校验对象状态等
一个示例,验证对象的获取和释放
- 被池化的对象
/**
* 被池化的对象, 此对象将放入对象池中
*
*/
public class BigObject {
private String key;
public BigObject(String key) {
this.key = key;
}
public BigObject() {}
}
- 被池化对象的工厂
/**
* 生产被池化对象的工厂
* @author yaowan
*
*/
public class BigObjectFactory extends BasePooledObjectFactory<BigObject> {
@Override
public BigObject create() throws Exception {
return new BigObject("1");
}
@Override
public PooledObject<BigObject> wrap(BigObject obj) {
return new DefaultPooledObject<BigObject>(obj);
}
/**
* testOnCreate,testOnBorrow之一设置为true时,
* objectPool.borrowObject()被调用时,会调用此方法
*
*/
@Override
public boolean validateObject(PooledObject<BigObject> p) {
//用一个随机数据来模拟对象是否失效
if (new Random().nextInt(10) < 6) {
System.out.println(Thread.currentThread().getName()+" 对象失效。。。。。。。");
return false;
}
System.out.println(Thread.currentThread().getName()+" 对象有效。。。。。。。");
return true;
}
}
- 使用对象池中的对象
public class BigObjectProvider {
private ObjectPool<BigObject> objectPool;
public BigObjectProvider(ObjectPool<BigObject> objectPool) {
this.objectPool = objectPool;
}
public void use() {
System.out.println(Thread.currentThread().getName() + " 准备取对象。。。。。。");
System.out.println(objectPool.getNumActive() + "," + objectPool.getNumIdle());
// 获得对应key的对象
BigObject connectionTest1 = null;
try {
connectionTest1 = objectPool.borrowObject();
System.out.println(Thread.currentThread().getName() + " borrowObject = {" + connectionTest1 + "}");
System.out.println(Thread.currentThread().getName() + " 已取得对象,正在使用中。。。。。。。");
Thread.sleep(2000);
} catch (NoSuchElementException e) {
System.out.println(convert2String(org.apache.commons.lang3.exception.ExceptionUtils.getStackFrames(e)));
//再次请求使用
use();
} catch (IllegalStateException e) {
//对象放回对象池出错,抛出来的异常
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (connectionTest1 != null) {
// 释放对象
try {
objectPool.returnObject(connectionTest1);
System.out.println(
Thread.currentThread().getName() + objectPool.getNumActive() + "," + objectPool.getNumIdle());
System.out.println(Thread.currentThread().getName() + " 归还对象。。。。。。。");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public static String convert2String(String[] strings) {
StringBuilder builder = new StringBuilder();
for (String string : strings) {
builder.append(string).append("\n");
}
return builder.toString();
}
}
/**
* 模拟使用过程
* 启动两个线程来竞争使用,通过打印的信息来熟知整个过程
*
*/
public class UseCase {
public static void main(String[] args) {
GenericObjectPoolConfig config = new GenericObjectPoolConfig();
//当“连接池”中active数量达到阀值时,即“链接”资源耗尽时,连接池需要采取的手段
config.setBlockWhenExhausted(true);
config.setMaxTotal(1);
//设置为true时,在GenericObjectPool<T>.borrowObject(long)方法调用获取不到对象时,会调用
//PooledObjectFactory<T>.validateObject(PooledObject<T>)验证是否要释放对象
//此时对象池中没有对象时,会抛出NoSuchElementException异常
config.setTestOnBorrow(true);
config.setTestOnCreate(true);
BigObjectFactory factory = new BigObjectFactory();
final ObjectPool<BigObject> objectPool = new GenericObjectPool<>(PoolUtils.synchronizedPooledFactory(factory),config);
final BigObjectProvider provider = new BigObjectProvider(objectPool);
final int execNum = 3;
new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i< execNum;i++) {
System.out.println(Thread.currentThread().getName()+" 第"+i+"次使用前");
provider.use();
System.out.println(Thread.currentThread().getName()+" 第"+i+"次使用后");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i< execNum;i++) {
System.out.println(Thread.currentThread().getName()+" 第"+i+"次使用前");
provider.use();
System.out.println(Thread.currentThread().getName()+" 第"+i+"次使用后");
}
}
}).start();
}
}
- 运行结果
Thread-1 第0次使用前
Thread-1 准备取对象。。。。。。
0,0
Thread-2 第0次使用前
Thread-2 准备取对象。。。。。。
0,0
Thread-1 对象失效。。。。。。。
java.util.NoSuchElementException: Unable to validate object
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:506)
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:363)
at org.poker.common.objectpool.BigObjectProvider.use(BigObjectProvider.java:23)
at org.poker.common.objectpool.UseCase$1.run(UseCase.java:41)
at java.lang.Thread.run(Thread.java:745)
Thread-1 准备取对象。。。。。。
0,0
Thread-1 对象有效。。。。。。。
Thread-1 borrowObject = {org.poker.common.objectpool.BigObject@6f8225a}
Thread-1 已取得对象,正在使用中。。。。。。。
Thread-10,1
Thread-1 归还对象。。。。。。。
Thread-1 第0次使用后
Thread-1 第1次使用前
Thread-1 准备取对象。。。。。。
Thread-2 对象失效。。。。。。。
1,0
Thread-2 对象有效。。。。。。。
Thread-2 borrowObject = {org.poker.common.objectpool.BigObject@19ebee92}
Thread-2 已取得对象,正在使用中。。。。。。。
Thread-20,1
Thread-2 归还对象。。。。。。。
Thread-2 第0次使用后
Thread-2 第1次使用前
Thread-2 准备取对象。。。。。。
Thread-1 对象失效。。。。。。。
1,0
Thread-1 对象有效。。。。。。。
Thread-1 borrowObject = {org.poker.common.objectpool.BigObject@39c46fff}
Thread-1 已取得对象,正在使用中。。。。。。。
Thread-10,1
Thread-1 归还对象。。。。。。。
Thread-1 第1次使用后
Thread-1 第2次使用前
Thread-1 准备取对象。。。。。。
Thread-2 对象失效。。。。。。。
1,0
Thread-2 对象有效。。。。。。。
Thread-2 borrowObject = {org.poker.common.objectpool.BigObject@1dd47977}
Thread-2 已取得对象,正在使用中。。。。。。。
Thread-20,1
Thread-2 归还对象。。。。。。。
Thread-2 第1次使用后
Thread-2 第2次使用前
Thread-2 准备取对象。。。。。。
Thread-1 对象失效。。。。。。。
1,0
Thread-1 对象失效。。。。。。。
java.util.NoSuchElementException: Unable to validate object
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:506)
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:363)
at org.poker.common.objectpool.BigObjectProvider.use(BigObjectProvider.java:23)
at org.poker.common.objectpool.UseCase$1.run(UseCase.java:41)
at java.lang.Thread.run(Thread.java:745)
Thread-1 准备取对象。。。。。。
0,0
Thread-1 对象有效。。。。。。。
Thread-1 borrowObject = {org.poker.common.objectpool.BigObject@507e8ea4}
Thread-1 已取得对象,正在使用中。。。。。。。
Thread-10,1
Thread-2 对象有效。。。。。。。
Thread-1 归还对象。。。。。。。
Thread-1 第2次使用后
Thread-2 borrowObject = {org.poker.common.objectpool.BigObject@507e8ea4}
Thread-2 已取得对象,正在使用中。。。。。。。
Thread-20,1
Thread-2 归还对象。。。。。。。
Thread-2 第2次使用后
通过在对象池中放入1个对象,来观察对象的获取和释放过程,以及在并发环境下对象安全问题