一种轻量级对象池的设计与实现
轻量级对象池Java版本的代码已经完成,明早更新博客,讲解设计思路及代码实现,先发个帖子占个位子~
=======================我是华丽的分隔线========================
好吧,我食言了,拖到现在才交稿,主要是这几天有些事情要处理,一直没来得及写,对不住前几位来访的同志啦~下面言归正转。
创建新对象并初始化的操作,可能会消耗很多时间,特别是当对象初始化的工作包含了大量费时的操作时,在需要大量生成对象的时候,就可能会对性能造成一些不可忽略的影响。要缓解这一问题,除了从底层硬件角度升级考虑之外,同样我们可以从软件层次采取一些必要的减少对象创建次数的编码技巧。恰当使用对象池化技术,可以有效地减少对象生成和初始化时的消耗,提高系统的运行效率。因此本文介绍一种轻量级对象池基本的设计与实现思路,参考了开源项目Jakarta Commons Pool的源代码,吸取了其中的精华,改进了一些细节,去除了一些繁杂的逻辑,最终形成了我们自己的一个轻量级的对象池组件,我称之为“OP-ObjectPool1.0”。
我们之前可能会听说过线程池、连接池或IP池等类似的概念。它们的共同特点都是池子里面存放有很多空闲可用的资源,根据具体的请求随需分配资源,请求处理完成则对资源进行回收。当用户请求数大于池中所能提供的资源时,则可考虑对请求进行阻塞以等待资源的回收,或者动态添加新的资源到池中以应对请求。如果我们将池子里面的东东拔高一个层次,不再特指某一个具体的类型,而就是抽象意义上的对象啦,那也就是所谓的对象池啦~
对象池的基本思想是:池中保存了若干的对象,根据请求实现按需分配,用完了就回收,重复使用,从而在一定程度上减少频繁创建对象所造成的开销。这个用户存储、分配和回收对象的容器称之为“对象池”。对于没有状态的对象,在重复使用之前,无需要进行任何处理,对于有状态的对象,在重复使用之前,必须要把它恢复到原始状态。当然在实现过程中,状态恢复应该是依据具体的情况、具体的环境、具体的类型而定的,我们并不能实现统一化的恢复操作,因此将之设计为接口。然而为了方便起见,可以直接将该对象抛弃,返回一个新创建的实例。
注意一个事实:并非所有对象都适合拿来池化à因为维护对象池本身也需要一定的开销。对生成时开销不大的对象进行池化,极可能适得其反出现维护对象池的开销远远大于生成新对象的开销,从而降低性能。看来使用对象池理念上虽然很合乎情理,但是还是要考虑具体的应用场景。
一、Jakarta Commons Pool组件简介
Jakarta Commons Pool是一个用于在Java程序中实现对象池化的组件。它的基本情况是:
(1)主要作者:Morgan Delagrange、Geir Magnusson、Craig McClanahan、Rodney Waldhoff、David Weinrich和Dirk Verbeeck
(2)最新版本:1.5.5
(3)所含包数:2个org.apache.commons.pool和org.apache.commons.pool.impl
(4)所含类数:21个(其中有4个抽象类和6个接口)
(5)适用平台:Java 2, Standard Edition.
(6)单纯地使用Pool组件不需要太多的Java 2的知识和经验,对语法和基本概念(对象、异常、类、接口、实例、继承和实现等)有一般了解即可。
(7)Jakarta Commons Pool下载地址: http://jakarta.apache.org/commons/pool
二、实现自己的对象池OP-ObjectPool1.0
参照Jakarta Commons Pool的设计思想,我们的对象池组件划分了三类接口:
(1)PoolableObjectFactory:用于管理被池化的对象的产生、恢复和销毁。
(2)ObjectPool:用于管理要被池化对象的借出和归还,并借助PoolableObjectFactory管理对象的产生、恢复和销毁。
(3)ObjectPoolFactory:用于生成大量类型、设置不同的ObjectPool。
OP-ObjectPool1.0的整体代码结构图如下,代码里有详细的注释:
三、创立PoolableObjectFactory
对象池组件利用PoolableObjectFactory来管理池化的对象,主要包括对象的产生、恢复和销毁。PoolableObjectFactory是定义的一个抽象接口,在实际使用过程中需要根据情况创建具体实现。
package com.java.pool; /** * 池化对象管理工厂接口PoolableObjectFactory:用于管理对象池中对象的创建、还原、销毁 * * Email:[email protected] * * @author MONKEY.D.MENG 2011-03-16 * */ public interface PoolableObjectFactory { /** * 生成新对象 * * @param clsType * 对象类型 * @return 新对象 */ public Object createObject(Class clsType); /** * 对象在使用过程中内部状态会发生变化,当归还对象池时可能需要将对象还原为原始状态 * * @return 将对象还原为原始状态 */ public Object clearObject(Object obj); /** * 销毁一个对象 * * @param obj * 要销毁的对象 */ public void destroyObject(Object obj); }
四、创立ObjectPool
在有了合适的PoolableObjectFactory之后,便可以开始请出ObjectPool来与之同台演出了。对象池组件本身包含了若干种现成的ObjectPool的实现,可以直接利用。如果觉得不合适,也可以根据实际情况自行创建。具体的创建方法,可以参考下面的源代码。
package com.java.pool; /** * 对象池接口ObjectPool:管理对象池中对象的借出、归还等必要的操作 * * Email:[email protected] * * @author MONKEY.D.MENG 2011-03-16 * */ public interface ObjectPool { /** * 从对象池中取出对象 * * @return 取出的对象 */ public Object borrowObject(); /** * 将对象返回给对象池 * * @param obj * 需要返回的对象 */ public void returnObject(Object obj); /** * 返回当前对象池所申请对象上限数目 * * @return 对象上限数目 */ public int getMaxNum(); /** * 返回对象池中已经借出的对象数目 * * @return 活跃对象数目 */ public int getActiveNum(); /** * 返回对象池中空闲的对象数目 * * @return 空闲对象数目 */ public int getIdleNum(); /** * 清除对象池中所有空闲对象 */ public void clear(); /** * 关闭对象池,并释放所占资源 */ public void close(); /** * 设置池化对象管理工厂用于管理对象池中的对象 * * @param factory * 池化对象管理工厂 */ public void setFactory(PoolableObjectFactory factory); }
五、创立ObjectPoolFactory
有时候,我们想生成类型和设置不同的对象池,就应该考试使用工厂设计模式了,ObjectPoolFactory就应运而生。通过设置不同的参数,调用createPool()方法,ObjectPoolFactory即可生成相应的对象池。
package com.java.pool; /** * 对象池工厂接口ObjectPoolFactory:采用工厂模式生产对象池 * * Email:[email protected] * * @author MONKEY.D.MENG 2011-03-16 * */ public interface ObjectPoolFactory { /** * 对象池工厂,用于生成对象池 * * @param factory * 池化对象管理工厂 * @param maxIdle * 对象池所申请的最大对象数目 * @param clsType * 对象类型 * @return */ public ObjectPool createPool(PoolableObjectFactory factory, int maxNum, Class clsType); }
六、创立基本的实现BasePoolableObjectFactory和BaseObjectPool
PoolableObjectFactory和ObjectPool定义了许多抽象的接口,可以适应不断变化的需求。然而,在大多数的情况下,这些接口可以直接实现,如不进行任何操作或者始终返回true,以节省一些不必要的操作。实现基本的BasePoolableObjectFacoty和BaseObjectPool能够能够简化编码工作。
package com.java.pool.base; import com.java.pool.PoolableObjectFactory; /** * 基础池化对象管理工厂抽象类BasePoolableObjectFactory:提供最基本的池化对象管理操作 * * Email:[email protected] * * @author MONKEY.D.MENG 2011-03-16 * */ public abstract class BasePoolableObjectFactory implements PoolableObjectFactory { /** * 生成新对象 * * @param clsType * 对象类型 * @return 新对象 */ public Object createObject(Class clsType) { try { return clsType.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return null; } /** * 对象在使用过程中内部状态会发生变化,当归还对象池时可能需要将对象还原为原始状态 * * @return 还原后的对象(这里直接创建了一个新对象) */ public Object clearObject(Object obj) { obj = null; return new Object(); } /** * 销毁一个对象 * * @param obj * 要销毁的对象 */ public void destroyObject(Object obj) { obj = null; } } package com.java.pool.base; import com.java.pool.ObjectPool; import com.java.pool.PoolableObjectFactory; /** * 基础对象池抽象类BaseObjectPool:提供最基本的对象池操作实现 * * Email:[email protected] * * @author MONKEY.D.MENG 2011-03-16 * */ public abstract class BaseObjectPool implements ObjectPool { /** 对象池是否已经关闭 */ private volatile boolean closed = false; /** * 返回对象池中空闲对象数目 * * @return -1 */ public int getMaxNum() { return -1; } /** * 返回对象池中已借出对象数目 * * @return -1 */ public int getActiveNum() { return -1; } /** * 清除对象池中所有空闲对象 */ public void clear() { } /** * 关闭对象池,设置状态为关闭 */ public void close() { closed = true; } /** * 设置池化对象管理工厂用于管理对象池中的对象 * * @param factory * 池化对象管理工厂 */ public void setFactory(PoolableObjectFactory factory) { } /** * 返回对象池是否关闭 * * @return 对象池关闭状态 */ protected final boolean isClosed() { return closed; } /** * 断言对象池是否处于开放状态 * * @throws IllegalStateException */ protected final void assertOpen() { if (isClosed()) { throw new IllegalStateException("【对象池处于关闭状态!无法进行相应操作!】"); } } }
七、创立各式各样的ObjectPool
对象池组件存储方式应该有很多种:如内存、数据库、文件等;存储的数据结构也可以多种多样:如栈、数组、链表等;存取的策略也可以多种多样:如先进先出FIFO、先进后出FILO等。至于实现可以依据具体情况而定,这里面只简介一种栈存储的StackObjectPool。
package com.java.pool.impl; import com.java.pool.ObjectPool; import com.java.pool.ObjectPoolFactory; import com.java.pool.PoolableObjectFactory; /** * 栈式对象池工厂类StackObjectPoolFactory:提供生产栈式对象池的工厂 * * Email:[email protected] * * @author MONKEY.D.MENG 2011-03-16 * */ public class StackObjectPoolFactory implements ObjectPoolFactory { /** 单例 */ private static StackObjectPoolFactory instance = null; /** * 单例模式 * * @return 栈式对象池工厂的单例 */ public static StackObjectPoolFactory getInstance() { if (null == instance) { instance = new StackObjectPoolFactory(); } return instance; } /** * 栈式对象池工厂,用于生成对象池 * * @param factory * 池化对象管理工厂 * @param maxIdle * 对象池所申请的最大对象数目 * @param clsType * 对象类型 * @return */ public ObjectPool createPool(PoolableObjectFactory factory, int maxNum, Class clsType) { return new StackObjectPool(factory, maxNum, clsType); } } package com.java.pool.impl; import java.util.NoSuchElementException; import java.util.Stack; import com.java.pool.ObjectPool; import com.java.pool.PoolableObjectFactory; import com.java.pool.base.BaseObjectPool; /** * 栈式对象池类StackObjectPool:提供栈式对象池的操作实现 * * Email:[email protected] * * @author MONKEY.D.MENG 2011-03-16 * */ public class StackObjectPool extends BaseObjectPool implements ObjectPool { /** 栈式对象池 */ protected Stack<Object> pool = null; /** 池化对象管理工厂 */ protected PoolableObjectFactory factory = null; /** 对象池中所存放对象的类型 */ private Class clsType = Object.class; /** 对象池中所能申请的对象数目上限 */ protected int maxNum = 100; /** 已借出对象数目 */ protected int activeNum = 0; /** * 构造函数,构建栈式对象池 * * @param factory * 池化对象管理工厂 * @param maxIdle * 对象池中空闲对象数目上限 * @param initIdleCapacity * 对象池初始化容量 */ public StackObjectPool(PoolableObjectFactory factory, int maxNum, Class clsType) { this.factory = factory; this.activeNum = 0; this.maxNum = maxNum; this.clsType = clsType; this.pool = new Stack<Object>(); } /** * 从对象池中取出对象 * * @return 取出的对象 */ public Object borrowObject() { synchronized (pool) { // 断言对象池是否处于开放状态 assertOpen(); Object currentObj = null; // 当前对象池有对象可用 if (!pool.empty()) { // 直接从对象池中取出对象 currentObj = pool.pop(); } // 当前对象池无可用对象 else { // 已借出对象数目小于对象池所要求对象数目最大值 if (activeNum < maxNum) { // 池化对象管理工厂为空,则无法生成新对象,抛出异常 if (null == factory) { throw new NoSuchElementException(); } // 由池化对象管理工厂生成新对象 currentObj = factory.createObject(clsType); // 生成对象为空则抛出异常 if (null == currentObj) { throw new NoSuchElementException( "StackObjectPool.borrowObject()返回NULL."); } } // 已借出对象数目到达对象池所要求对象数目最大值 else { // 等待其它对象返回对象到池中 try { pool.wait(); } catch (InterruptedException e) { e.printStackTrace(); } // 从池中取出对象 currentObj = pool.pop(); } } // 增加已借出对象数目 ++activeNum; return currentObj; } } /** * 将对象返回给对象池 * * @param obj * 需要返回的对象 */ public void returnObject(Object obj) { synchronized (pool) { // 断言对象池是否处于开放状态 assertOpen(); // 确保对象具有正确的类型 if (obj.getClass() == clsType) { pool.push(obj); // 减少已借出对象数目 --activeNum; // numActive发生改变,通知其他阻塞线程 pool.notifyAll(); } else { throw new IllegalArgumentException("类型" + obj.getClass().getName() + "要求类型" + clsType.getName() + "不匹配!"); } } } /** * 清除对象池中所有空闲对象 */ public synchronized void clear() { pool.clear(); } /** * 关闭对象池,并释放所占资源 */ public synchronized void close() { super.close(); clear(); pool = null; factory = null; clsType = Object.class; maxNum = -1; activeNum = -1; } /** * 返回对象池中已经借出的对象数目 * * @return 活跃对象数目 */ public synchronized int getActiveNum() { return activeNum; } /** * 返回当前对象池中可用的空闲对象数目 * * @return 空闲对象数目 */ public synchronized int getMaxNum() { return maxNum; } /** * 返回对象池中空闲的对象数目 * * @return 空闲对象数目 */ public synchronized int getIdleNum() { return pool.size(); } /** * 设置池化对象管理工厂用于管理对象池中的对象 * * @param factory * 池化对象管理工厂 */ public synchronized void setFactory(PoolableObjectFactory factory) { this.factory = factory; } public synchronized Stack<Object> getPool() { return pool; } public synchronized void setPool(Stack<Object> pool) { this.pool = pool; } public synchronized Class getClsType() { return clsType; } public synchronized void setClsType(Class clsType) { this.clsType = clsType; } public synchronized void setMaxNum(int maxNum) { this.maxNum = maxNum; } public synchronized void setActiveNum(int activeNum) { this.activeNum = activeNum; } public synchronized PoolableObjectFactory getFactory() { return factory; } }
八、创立String类型的PoolableObjectFactory类
为了对String类型的池化对象进行管理,我们创建了StringPoolableObjectFactory实现PoolableObjectFactory的接口。
package com.java.pool.impl; import com.java.pool.base.BasePoolableObjectFactory; /** * String类型管理工厂类StringPoolableObjectFactory:提供对String类型对象的管理 * * Email:[email protected] * * @author MONKEY.D.MENG 2011-03-16 * */ public class StringPoolableObjectFactory extends BasePoolableObjectFactory { /** 单例 */ private static StringPoolableObjectFactory instance = null; /** * 单例模式 * * @return 基础池化对象管理工厂的单例 */ public static StringPoolableObjectFactory getInstance() { if (null == instance) { instance = new StringPoolableObjectFactory(); } return instance; } /** * 对象在使用过程中内部状态会发生变化,当归还对象池时可能需要将对象还原为原始状态 * * @return 新对象 */ public Object clearObject(Object obj) { obj = null; return new String(); } }
九、创建测试类
这样,基本上我们就实现了一个轻量级的对象池。还需要对它进行一些测试:
package com.java.pool.test; import com.java.pool.ObjectPool; import com.java.pool.PoolableObjectFactory; import com.java.pool.impl.StackObjectPoolFactory; import com.java.pool.impl.StringPoolableObjectFactory; /** * 对象池测试类Test * * Email:[email protected] * * @author MONKEY.D.MENG 2011-03-16 * */ public class Test { public static void main(String[] args) { // 创建String类型的池化对象管理工厂 PoolableObjectFactory factory = StringPoolableObjectFactory .getInstance(); // 创建栈式对象池 ObjectPool pool = StackObjectPoolFactory.getInstance().createPool( factory, 100, String.class); // 多线程测试 for (int index = 1; index <= 20; ++index) { new MyThread(pool, factory).start(); } } } /** * 封装线程用于测试对象池 * * Email:[email protected] * * @author MONKEY.D.MENG 2011-03-16 * */ class MyThread extends Thread { /** 对象池 */ private ObjectPool pool = null; /** 池化对象管理工厂 */ private PoolableObjectFactory factory = null; /** * * @param pool * 对象池 * @param factory * 池化对象管理工厂 */ public MyThread(ObjectPool pool, PoolableObjectFactory factory) { this.pool = pool; this.factory = factory; } public void run() { // 获取新对象 String buffer = (String) pool.borrowObject(); // 必要的业务逻辑神马的 buffer = "【" + this.currentThread() + ":MONKEY.D.MENG】-->"; System.out.println(buffer + "/t活跃对象数:" + pool.getActiveNum() + "/t空闲对象数:" + pool.getIdleNum()); // 返回对象 pool.returnObject(factory.clearObject(buffer)); } public ObjectPool getPool() { return pool; } public void setPool(ObjectPool pool) { this.pool = pool; } public PoolableObjectFactory getFactory() { return factory; } public void setFactory(PoolableObjectFactory factory) { this.factory = factory; } } 【Thread[Thread-2,5,main]:MONKEY.D.MENG】--> 活跃对象数:2 空闲对象数:0 【Thread[Thread-1,5,main]:MONKEY.D.MENG】--> 活跃对象数:2 空闲对象数:1 【Thread[Thread-6,5,main]:MONKEY.D.MENG】--> 活跃对象数:4 空闲对象数:0 【Thread[Thread-4,5,main]:MONKEY.D.MENG】--> 活跃对象数:3 空闲对象数:0 【Thread[Thread-0,5,main]:MONKEY.D.MENG】--> 活跃对象数:2 空闲对象数:1 【Thread[Thread-5,5,main]:MONKEY.D.MENG】--> 活跃对象数:3 空闲对象数:1 【Thread[Thread-8,5,main]:MONKEY.D.MENG】--> 活跃对象数:3 空闲对象数:2 【Thread[Thread-3,5,main]:MONKEY.D.MENG】--> 活跃对象数:3 空闲对象数:1 【Thread[Thread-14,5,main]:MONKEY.D.MENG】--> 活跃对象数:3 空闲对象数:1 【Thread[Thread-7,5,main]:MONKEY.D.MENG】--> 活跃对象数:2 空闲对象数:2 【Thread[Thread-12,5,main]:MONKEY.D.MENG】--> 活跃对象数:1 空闲对象数:3 【Thread[Thread-10,5,main]:MONKEY.D.MENG】--> 活跃对象数:1 空闲对象数:3 【Thread[Thread-9,5,main]:MONKEY.D.MENG】--> 活跃对象数:1 空闲对象数:3 【Thread[Thread-11,5,main]:MONKEY.D.MENG】--> 活跃对象数:1 空闲对象数:3 【Thread[Thread-16,5,main]:MONKEY.D.MENG】--> 活跃对象数:1 空闲对象数:3 【Thread[Thread-15,5,main]:MONKEY.D.MENG】--> 活跃对象数:2 空闲对象数:2 【Thread[Thread-13,5,main]:MONKEY.D.MENG】--> 活跃对象数:2 空闲对象数:2 【Thread[Thread-18,5,main]:MONKEY.D.MENG】--> 活跃对象数:1 空闲对象数:3 【Thread[Thread-17,5,main]:MONKEY.D.MENG】--> 活跃对象数:1 空闲对象数:3 【Thread[Thread-19,5,main]:MONKEY.D.MENG】--> 活跃对象数:1 空闲对象数:3
十、什么时候不要池化
采用对象池化的本意,是要通过减少对象生成的次数,减少花在对象初始化上面的开销,从而提高整体的性能。然而池化处理本身也要付出代价,因此,并非任何情况下都适合采用对象池化。
Dr. Cliff Click在JavaOne 2003上发表的《Performance Myths Exposed》中,给出了一组其它条件都相同时,使用与不使用对象池化技术的实际性能的比较结果。他的实测结果表明:
对于类似Point这样的轻量级对象,进行池化处理后,性能反而下降,因此不宜池化;对于类似Hashtable这样的中量级对象,进行池化处理后,性能基本不变,一般不必池化(池化会使代码变复杂,增大维护的难度);对于类似JPanel这样的重量级对象,进行池化处理后,性能有所上升,可以考虑池化。
根据使用方法的不同,实际的情况可能与这一测量结果略有出入。在配置较高的机器和技术较强的虚拟机上,不宜池化的对象的范围可能会更大。不过,对于像网络和数据库连接这类重量级的对象来说,目前还是有池化的必要。
基本上,只在重复生成某种对象的操作成为影响性能的关键因素的时候,才适合进行对象池化。如果进行池化所能带来的性能提高并不重要的话,还是不采用对象池化技术,以保持代码的简明,而使用更好的硬件和更棒的虚拟机来提高性能为佳。
十一、结束语
恰当地使用对象池化,可以有效地降低频繁生成某些对象所造成的开销,从而提高整体的性能。而借助对象池组件,可以有效地减少花在处理对象池化上的工作量,进而,向其它重要的工作里,投入更多的时间和精力。