工作中我们都会用到各种各样的池技术,像数据库连接池、Redis连接池、线程池等。池技术的原理简单来说就是将创建好的对象缓存到一个“池子”里,需要使用对象时就从池子里获取,不用再走重复的创建、销毁等过程,以提高应用程序的性能。接下来我们DIY一个对象池,从而了解其实现原理。
- 定义一个对象池。一个最简单的对象池就两个方法,借用对象和归还对象。
public class ObjectPool<T> {
/**
* 空闲对象,PoolObject是对对象的进一步封装,方便添加扩展参数
*/
private LinkedBlockingDeque<PoolObject<T>> idleObjects;
/**
* 所有的对象
*/
private Map<Integer, PoolObject<T>> allObjects;
/**
* 池大小
*/
private Integer poolSize;
/**
* 对象工厂,用于创建对象
*/
private PoolObjectFactory<T> factory;
public ObjectPool(Integer poolSize, PoolObjectFactory<T> factory) {
this.poolSize = poolSize;
this.factory = factory;
idleObjects = new LinkedBlockingDeque<>(poolSize);
allObjects = new ConcurrentHashMap<>();
}
/**
* 从连接池里拿一个对象
* @return
*/
public T borrowObject() {
PoolObject<T> p = null;
while (true) {
// 从空闲队列中取对象,如果取到则直接返回
p = idleObjects.pollFirst();
if (p != null) {
System.out.println(Thread.currentThread().getName() + "获取到对象:" + p);
return p.getObject();
}
synchronized (this) {
if (poolSize > allObjects.size()) {
// 如果还没有达到池的大小则执行创建对象,并将创建好的对象放入池中
p = factory.makeObject();
allObjects.put(System.identityHashCode(p.getObject()), p);
idleObjects.push(p);
} else {
// 如果池已经满了,则进入等待
try {
System.out.println(Thread.currentThread().getName() + "进入等待");
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
/**
* 归还对象
* @param obj
*/
public void returnObject(T obj) {
PoolObject<T> p = allObjects.get(System.identityHashCode(obj));
if (p == null) {
return;
}
System.out.println(Thread.currentThread().getName() + "归还对象:" + p);
idleObjects.add(p);
synchronized (this) {
this.notifyAll();
}
}
}
- 对象的封装。这里对创建的对象做一些封装,方便可以扩展其他参数。
public class PoolObject<T> {
/**
* 对象
*/
private T object;
public PoolObject(T object) {
this.object = object;
}
public T getObject() {
return object;
}
public void setObject(T object) {
this.object = object;
}
}
- 创建对象的工厂
public interface PoolObjectFactory<T> {
/**
* 创建对象
* @return
*/
PoolObject<T> makeObject();
}
- 测试
public static void main(String[] args) {
ObjectPool<Integer> pool = new ObjectPool<>(2, new PoolObjectFactory<Integer>() {
@Override
public PoolObject<Integer> makeObject() {
return new PoolObject<>(1);
}
});
for (int i = 0; i < 5; i++) {
new Thread(()->{
Integer obj = pool.borrowObject();
try {
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
pool.returnObject(obj);
}).start();
}
}
执行效果:
Thread-2获取到对象:com.gitee.bailiny.PoolObject@579b39be
Thread-3获取到对象:com.gitee.bailiny.PoolObject@67b23274
Thread-0进入等待
Thread-4进入等待
Thread-1进入等待
Thread-3归还对象:com.gitee.bailiny.PoolObject@67b23274
Thread-4进入等待
Thread-1获取到对象:com.gitee.bailiny.PoolObject@67b23274
Thread-0进入等待
Thread-2归还对象:com.gitee.bailiny.PoolObject@579b39be
Thread-0获取到对象:com.gitee.bailiny.PoolObject@579b39be
Thread-4进入等待
Thread-0归还对象:com.gitee.bailiny.PoolObject@579b39be
Thread-4获取到对象:com.gitee.bailiny.PoolObject@579b39be
Thread-1归还对象:com.gitee.bailiny.PoolObject@67b23274
Thread-4归还对象:com.gitee.bailiny.PoolObject@579b39be
以上就是自定义一个对象池的最简单实现,实际上代码参考了common-pool2中GenericObjectPool的实现。只不过在common-pool2中还有更丰富的功能,比如等待超时、最大容量、空闲容量、自定义参数配置等。