意图
对象池的使用可以提供显著的性能提升;该模式最适合的场景有:
- 实例化一个类实例的成本很高
- 一个类的实例化率很高
- 在任何时刻,使用中的实例个数都是很少的
上面这些是我直接从网上翻译的,看起来比较难懂,说的不够清楚,其实主要意思我们从该模式的字面意思就能懂了,对象池也叫资源池,就是我们提前初始化一批对象实例,这样我们在后续使用的时候,就可以直接用,不用再创建,而且这些对象可以重用。其实在这里,我们就是把一些相对重量的资源(这里我们用资源来表示目标的对象)提前准备好,而且这类对象的生命周期相对长(一个要考虑的问题就是由于生命周期长,我们是不是应该把这类对象直接分配到老年代?),后续直接使用就可以了,或者后续我们可以按需再扩容。比如,数据库连接池,线程池等;对象池本身一般以单例的形式存在。
解决问题
对象池通过缓存一批对象,来减少对象的创建耗时,同时我们可以用相同的策略来管理这批对象,我们也可以添加相应的并发控制以支持并发访问
类图结构
代码样例
首先我们有一个相对重量级的类型HeavyObject
,由于该对象的构造成本较高,这里直接不允许客户端显式构造该对象了(private),然后我们使用一个HeavyObjectPool
来接管对象的创建,释放相关的任务(这里的释放逻辑其实是归还到对象池),而HeavyObjectPool
则实现了Poolable
接口,该接口代码如下:
public interface Poolable {
Optional getPooledInstance();
void release(E e);
}
Poolable
接口只有两个方法,一个是获取池化的实例对象,另一个是释放操作,也可以理解为归还(因为对象的生命周期管理交给了对象池,因此这里实际是把用完的对象归还回去)。
接下来是具体的HeavyObject
和HeavyObjectPool
的实现逻辑,其实这里把HeavyObject
的构造方法指定为private,并没有什么特别的用意,跟本模式是没有关系的。对象池说白了就是客户端不主动创建具体对象了,而是通过对象池来创建,也就是说客户端把对目标对象的生命周期管理的控制权交给的对象池,由对象池来管理,同时对象池天然提供了缓存的特性,使得后续客户端在使用对象时,省去了创建对象的开销(实际上可能因为对象池具体实现,这里是不确定的,因为对象池中的对象虽然数量可能不多,但也可能是按需创建的,即当前对象池中暂时没有可用对象时,直接创建新对象,而不是等对象可用)
public class HeavyObject {
private int intState = 0;
private HeavyObject() {
for (int i = 0; i < 100_000; i++) {
for (int j = 0; j < 1000; j++) {
intState ++;
}
}
}
public synchronized static Poolable createInstancePool(int count) {
return new HeavyObjectPool(count);
}
private static class HeavyObjectPool implements Poolable{
private int DEFAULT_INSTANCE_COUNTS = 5;
private BlockingQueue pools = new LinkedBlockingQueue<>();
public HeavyObjectPool(int count) {
if (count <= 0)
count = DEFAULT_INSTANCE_COUNTS;
for (int i = 0; i < count; i++) {
pools.offer(new HeavyObject());
}
}
@Override
public Optional getPooledInstance() {
try {
return Optional.of(pools.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
return Optional.empty();
}
@Override
public void release(HeavyObject heavyObject) {
pools.offer(heavyObject);
}
}
}