对象池模式

科技工作者之家 2020-11-17

对象池模式(The Object Pool Pattern)是单例模式的一个变种,它提供了获取一系列相同对象实例的入口。当你需要对象来代表一组可替代资源的时候就变的很有用,每个对象每次可以被一个组件使用。

举例在许多项目中,有时候对象的实例数目可能会有限制。请看下面例子:

在一个追踪图书馆书的系统中,创建或者克隆Book对象都不适用于现实中的图书馆中的书。同样的如果使用单例模式也不行,因为图书馆里可不止一本书。

图书管中的每一本书都有可能在某个时候被读者借出并且以后不能再被人使用直到归还。当书在库的时候读者可以立即借出,但是当库存耗尽的时候,任何想要再借这本书的人都必须等到某个人还书或者图书馆增加库存。

理解方法对象池模式管理一个可代替对象的集合。组件从池中借出对象,用它来完成一些任务并当任务完成时归还该对象。被归还的对象接着满足请求,不管是同一个组件还是其他组件的请求。对象池模式可以管理那些代表的现实资源或者通过重用来分摊昂贵初始化代价的对象。

第二步操作就是借出。

第三步操作是组件用借出的对象来完成一些任务。这时候并不需要对象池再做什么,但是这也意味着该对象将被租借一段时间并且不能在被其他组件借出。

第四步操作就是归还,组件归还借出的对象这样可以继续满足其他的租借请求。

在一个多线程的应用中,第二,第三,第四步操作都有可能发生并发操作。多线程的组件中分享对象导致了潜在的并发问题。也存在一种情况就是当所有对象都被借出时不能满足接下来的请求,对象池必须应对这些请求,不管是告诉组件已经没有对象可借还是允许组件等待直到有归还的对象。1

优点复用池中对象,没有分配内存和创建堆中对象的开销, 没有释放内存和销毁堆中对象的开销, 进而减少垃圾收集器的负担, 避免内存抖动;不必重复初始化对象状态, 对于比较耗时的constructor和finalize来说非常合适;

缺点(1)现在Java的对象分配操作不比c语言的malloc调用慢, 对于轻中量级的对象, 分配/释放对象的开销可以忽略不计;

(2)并发环境中, 多个线程可能(同时)需要获取池中对象, 进而需要在堆数据结构上进行同步或者因为锁竞争而产生阻塞, 这种开销要比创建销毁对象的开销高数百倍;

(3)由于池中对象的数量有限, 势必成为一个可伸缩性瓶颈;

(4)很难正确的设定对象池的大小, 如果太小则起不到作用, 如果过大, 则占用内存资源高, 可以起一个线程定期扫描分析, 将池压缩到一个合适的尺寸以节约内存,但为了获得不错的分析结果, 在扫描期间可能需要暂停复用以避免干扰(造成效率低下), 或者使用非常复杂的算法策略(增加维护难度);

(5)设计和使用对象池容易出错, 设计上需要注意状态同步, 这是个难点, 使用上可能存在忘记归还(就像c语言编程忘记free一样), 重复归还(可能需要做个循环判断一下是否池中存在此对象, 这也是个开销), 归还后仍旧使用对象(可能造成多个线程并发使用一个对象的情况)等问题。2

应用对象池模式经常用在频繁创建、销毁对象(并且对象创建、销毁开销很大)的场景,比如数据库连接池、线程池、任务队列池等。

本词条内容贡献者为:

黄伦先 - 副教授 - 西南大学

科技工作者之家

科技工作者之家APP是专注科技人才,知识分享与人才交流的服务平台。