对象池的定义:
对象的实例化是最耗费性能的操作之一,这在过去是个大问题,现在不用再过分关注它。但当我们处理封装外部资源的对象(如数据库连接)时,对象的创建操作则会耗费很多资源。
解决方案是重用和共享这些创建成本高昂的对象,这称为对象池模式(创建型模式)。
解决方案是重用和共享这些创建成本高昂的对象,这称为对象池模式(创建型模式)。
直接上代码:
1、对象工厂类
package com.zc.demo; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.PooledObjectFactory; import org.apache.commons.pool2.impl.DefaultPooledObject; public class SeatNumFactory implements PooledObjectFactory<SeatNum> { // 创建对象 或 引用现有对象 @Override public PooledObject<SeatNum> makeObject() throws Exception { System.out.println("【创建对象】"); return new DefaultPooledObject<SeatNum>(new SeatNum()); } // 销毁对象 @Override public void destroyObject(PooledObject<SeatNum> pooledObject) throws Exception { System.out.println("【销毁对象】,剩余数量="+pooledObject.getObject().num); pooledObject.deallocate();// 销毁 } // 验证对象 @Override public boolean validateObject(PooledObject<SeatNum> pooledObject) { System.out.println("【验证对象】数量="+pooledObject.getObject().num); return pooledObject.getObject().num > 0; // 对象的一个销毁条件 } // 活动对象 @Override public void activateObject(PooledObject<SeatNum> pooledObject) throws Exception { System.out.println("【活动对象】初始化前剩余数量="+pooledObject.getObject().num); // pooledObject.getObject().num = 100; // System.out.println("【活动对象】初始化后剩余数量="+pooledObject.getObject().num); } // 停用(归还)对象 @Override public void passivateObject(PooledObject<SeatNum> pooledObject) throws Exception { System.out.println("【停用对象】数量="+pooledObject.getObject().num); } }
2、对象类
package com.zc.demo; import org.apache.commons.pool2.impl.GenericObjectPool; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; public class SeatNum { int num = 100; void doSomething() { System.out.println("引用前的座位数量:" + this.num); num -= 40; System.out.println("引用后的座位数量:" + this.num); } public static void main(String[] args) { GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig(); genericObjectPoolConfig.setMaxTotal(5); genericObjectPoolConfig.setMinIdle(2);//最小空闲数量,也是默认初始化的数量 genericObjectPoolConfig.setMaxIdle(2); genericObjectPoolConfig.setMinEvictableIdleTimeMillis(1000); genericObjectPoolConfig.setTestOnBorrow(true);// 引用对象后(对象已存在,且重复使用)调用验证validateObject(常用) // genericObjectPoolConfig.setTestOnReturn(true);// 停用对象前(对象已存在)调用验证validateObject(常用) // genericObjectPoolConfig.setTestOnCreate(true);// 创建对象(对象未存在)时验证validateObject(极少情况采用) // genericObjectPoolConfig.setTestWhileIdle(true);// 对象一直空闲时验证validateObject(极少情况采用) GenericObjectPool<SeatNum> objectPool = new GenericObjectPool<>(new SeatNumFactory(), genericObjectPoolConfig); SeatNum powerBank = null; for (int i = 0; i < 10; i++) { try { objectPool.preparePool();// 默认初始化 System.out.println("====================【" + i + "】==================="); powerBank = objectPool.borrowObject(); powerBank.doSomething(); } catch (Exception e) { e.printStackTrace(); } finally { if (powerBank != null) { objectPool.returnObject(powerBank); } } } } }
对象池的优点:
(1)复用池中对象
(2)消除创建对象、回收对象 所产生的内存开销、cpu开销以及(若跨网络)产生的网络开销.
对象池的缺点:
(1)现在Java的对象分配操作不比c语言的malloc调用慢, 对于轻中量级的对象, 分配/释放对象的开销可以忽略不计;
(2)并发环境中, 多个线程可能(同时)需要获取池中对象, 进而需要在堆数据结构上进行同步或者因为锁竞争而产生阻塞, 这种开销要比创建销毁对象的开销高数百倍;
(3)由于池中对象的数量有限, 势必成为一个可伸缩性瓶颈;
(4)很难正确的设定对象池的大小, 如果太小则起不到作用, 如果过大, 则占用内存资源高, 可以起一个线程定期扫描分析, 将池压缩到一个合适的尺寸以节约内存,但为了获得不错的分析结果, 在扫描期间可能需要暂停复用以避免干扰(造成效率低下), 或者使用非常复杂的算法策略(增加维护难度);
(5)设计和使用对象池容易出错, 设计上需要注意状态同步, 这是个难点, 使用上可能存在忘记归还(就像c语言编程忘记free一样), 重复归还(可能需要做个循环判断一下是否池中存在此对象, 这也是个开销), 归还后仍旧使用对象(可能造成多个线程并发使用一个对象的情况)等问题;