`

简化对象池

 
阅读更多
在Java中开发一个普遍性的对象池架构可以更好地利用资源,并可以使初始化的成本达到最小。
——by Karthik Rangaraju

大多数人对直接或间接运用对象池来连接一个数据库都很熟悉。但通常你也可以将对象放入对象池中从而节省重要的资源、提高程序的效率并控制对不充足资源的访问。出于对设计、成本或性能的考虑,放入对象池中的对象通常是有限的。它们或者是初始化成本很高的对象,或者是很少用的对象。运用对象池,我们就可以管理竞争性客户端对有限的对象集的访问。

Sun公司的Java HotSpot技术开发小组建议,Java程序员不要用他们自己的对象池。作为替代,它建议他们用Java虚拟机(JVM)中的改进的内存管理和对象分配。虽然HotSpot比以前的JVMs在管理对象创建和销毁方面更有效,但它不能使以JVM之外的资源为依托的对象的创建达到最优。所以,尽管HotSpot的性能有所提高,但你仍可以从对象池受益。

对象池主要是可以更好地运用你的资源。例如,设想有相当多的客户要有效地运用很少的数据库或网络连接。通过限制对对象的访问(只在客户端需要的时候才能访问对象),你就可以释放资源,让其它客户端使用。通过对象池提高对象的利用率通常可以提高系统性能。

你可以用对象池使初始化的成本达到最小。典型的例子包括数据库、网络连接和线程。这样的连接通常需要很多时间来初始化。一旦创建了这些连接,你就可以重用它们,从而极大地节省了成本。因此,你可以将初始化成本很高(从时间、内存或其它资源方面考虑)的对象放入对象池中。例如,大多数容器都将Enterprise JavaBeans放入对象池中,从而避免重复的资源分配和状态初始化。

让我们来考虑一个普遍性的可以容纳任何Java对象的对象池架构。这个架构包含一个主要的PoolManager类,它可以让我们访问对象池中的对象。通过调用PoolManager.get(),你就可以得到对象,该方法从对象池读取对象,将它封装在一个PooledResource容器对象中,并返回对该容器的引用。你可以通过调用PooledResource.release()将对象返回到对象池。为了使PoolManager可以扩展,我运用了Strategy设计模式(见Design Patterns: Elements of Reusable Object-Oriented Software一书中的描述),它可以让应用程序定义对象创建和对象池容量管理策略。ObjectManager类是实现下面这些功能的关键:应用程序必须扩展ObjectManager,提供创建逻辑将对象放入对象池中,并提供策略来加大或减小对象池的容量,而且也可以定期刷新对象池中的对象。

PoolManager为不同的对象池容量管理策略提供了构造器(见列表1)。

构建PoolManager

列表 1. PoolManager类可以让我们访问对象池中的对象,并为不同的对象池容量管理策略提供构造器。

/**
 * Constructs a new PoolManager with the specified 
 * ObjectManager and specified initial size.
 * @param pManager reference to ObjectManager to be used.
 * @param pMaxSize max size of pool (once this limit is 
 * reached, additional requests result in 
 * ObjectManager.increasePoolSize() being called).
 * @param pInitialSize number of objects to add to the 
 * pool immediately. The objects are obtained by calling
 * ObjectManager.getNewObject().
 * @see ObjectManager#increasePoolSize(int)
 * @see ObjectManager#getNewObject()
 */
 public PoolManager(ObjectManager pManager, 
                  int pMaxSize, 
                  int pInitialSize)

/**
 * Constructs a pool with zero initial size
 * @param pManager reference to ObjectManager to be 
 * used.
 * @param pMaxSize max size of pool (once this limit
 * is reached, additional requests result in 
 * ObjectManager.increasePoolSize() being called).
 * @see #PoolManager(ObjectManager, int, int)
 * @see ObjectManager#increasePoolSize(int)
 */
 public PoolManager(ObjectManager pManager, 
                        int pMaxSize)
  
/**
 * Construct a pool with zerio max and initial size. 
 * This lets ObjectManager.increasePoolSize() dictate 
 * the pool sizing policy.
 * @param pManager reference to ObjectManager to be used
 * @see #PoolManager(ObjectManager, int, int)
 */
 public PoolManager(ObjectManager pManager)

当PoolManager是用初始的、最大的容量构建的时,它就调用ObjectManager.create()来创建一个初始的对象集。当客户端请求一个对象而它的初始集已经满了时,对象池的容量就会加大,直到达到最大。这时候,新的请求就会调用ObjectManager.increasePoolSize()。只要increasePoolSize()不被覆盖,请求就会被阻塞,直到一个对象被释放回对象池。

通过确定最大容量来构建PoolManager会导致lazy initialization,这就是说在第一次请求对象时创建对象,然后就将对象保留在对象池中。当不用初始或最大容量构建PoolManager时,执行ObjectManager.increasePoolSize()就可以指定对象池的容量——因为当对象池空了时,它是从PoolManager.get()来调用的(见列表2)。

读取对象

列表 2. PoolManager.get()从对象池读取对象,根据需要创建新的对象并加大对象池容量。

  /** 
  * Retrieves a PooledObject from the pool.
  * @return reference to PooledObject obtained from the 
  * pool.
  * @throws InterruptedException if the thread is 
  * interrupted when waiting.
  * @throws PoolDisabledException if disable() is called 
  * while the thread is waiting.
  */
 public PooledObject get() throws PoolDisabledException,
                                    InterruptedException
 {
     PooledObject resource = null;

     synchronized (this)
     {
         if (mEnabled == false)
         {
             throw new PoolDisabledException
                        ("Pool disabled");
         }
         // mWaitCount is used to keep track of total 
       // requests blocked.
         mWaitCount++;
         if (mPool.isEmpty() && mCurrentSize < mMaxSize)
         {
             add(mObjectManager.create());
             mCurrentSize++;
         }
         else if (mPool.isEmpty())
         {
             mObjectManager.increasePoolSize(mWaitCount);
         }
         while (mEnabled && mPool.isEmpty())
         {
             try
             {
                 this.wait();
             }
             catch (InterruptedException e)
             {
                 mWaitCount--;
                 throw e;
             }
         }
         mWaitCount--;
         if (mEnabled)
         {
             resource = (PooledObject)mPool.dequeue();
         }
         else
         {
             throw new PoolDisabledException
                        ("Pool disabled");
         }
     }
     mReleaseStrategy.preAcquire(resource.get());
     
     return resource;
 }


PoolManager.get()方法也将未完成的请求数量传递到ObjectManager.increasePoolSize()中,这样你就可以延迟创建新的对象,直到达到一个特定的极限。这就方便了复杂的对象池容量策略的实现。

PoolManager.get()方法查看对象池是否被激活,并增加等待实现的请求数量。它也查看对象池是否是空的。如果还有空间来加大对象池,它就调用ObjectManager.create()来创建新的对象,添加到对象池中并返回给调用者。否则,它调用ObjectManager的increasePoolSize(),传入等待的请求数量。如果对象池仍是空的——因为ObjectManager.increasePoolSize()没有创建新的对象——请求就会被阻塞,直到一个对象被释放回到对象池中。

PoolManager定期调用ObjectManager.decreasePoolSize()来查看是否可以回收对象池中不用的对象。你可以覆盖getDecrementInterval()方法来改变缺省的30分钟的时间间隔。在执行decreasePoolSize()时,得到或释放对象的请求就被阻塞。另外,decreasePoolSize()可以调用shrinkPool()方法,它可以将一个对象移出对象池并提供对它的引用。PoolManager也可以让你刷新对象池中的对象——例如,确保TCP/IP连接没有超时。PoolManager定期(缺省情况下,每隔五分钟)调用ObjectManager.refresh(),传入一个iterator,给对象提供只读访问。在执行ObjectManager.refresh()时,从对象池得到或释放对象的请求会被阻塞。

最后,PoolManager提供hooks(通过PoolReleaseStrategy),在客户端得到对象前可以在对象池中处理对象。这就为我们提供了方便,使我们可以在运用对象前初始化对象,在它们返回对象池前重新设置它们的状态——处理它们。例如,你可以查看数据库事务处理隔离级别(isolation level)或重新设置错误代码。

我们这里讲述的对象池架构具有普遍性,它适合不同应用程序的需要。可以让你控制对象池的容量、对象填充策略和对象状态。一个设计良好的对象池可以极大地提高你的应用程序的效率,不管从速度方面考虑,还是从资源利用率方面考虑,该架构都可以提供这样的好处。

分享到:
评论

相关推荐

    内存池和对象池C++实现

    这是参考了BOOST的内存池和对象池的实现的,基本上和BOOST的代码一样,不同的是: (1)风格不一样; (2)把实现的代码尽量简化了 (3)大多代码都加了详尽的注释(注释是中文的) 个人认为这份代码的意义在于如果...

    内存池和对象池C++实现(v20150527)

    这是参考了BOOST的内存池和对象池的实现的,基本上和BOOST的代码一样,不同的是: (1)风格不一样; (2)把实现的代码尽量简化了 (3)大多代码都加了详尽的注释(注释是中文的) 个人认为这份代码的意义在于如果...

    EasyObjectsPool(一个简单的Unity对象池插件)

    EasyObjectsPool是一个轻量级的Unity对象池插件,它简单好用,专为简化游戏对象的创建和销毁而设计,旨在提高游戏性能和优化内存使用。

    dotNet-turbo:.NET应用程序的有用类的集合(对象池,线程池,异步处理,队列,集合,多线程基元等)

    库包含许多可重用的基类: -易于使用的对象池; -帮助项目处理并行化; -快速线程池,可动态调整线程数; -简化常见的线程启动/停止方案; 极快的轻量级信号灯; 具有阻塞的线程安全队列(比BlockingCollection快5...

    数据库连接池druid,c3p0,jdbctemplate,jar包.rar

    数据库连接池jar包,包含c3p0、druidjar包和依赖jar包,c3p0通过配置文件xml或者properties读取连接对象 druid通过properties读取连接,使用springJDBC JdbcTempalte简化sql操作

    proposal-object-iteration:ECMA TC39建议,用于简化对象映射

    但是默认情况下,对象是不可迭代的,因此,如果不先将对象转换为数组 ,那么对象上的许多有价值的收集方法就无法用于对象。 拟议的解决方案 提供在对象上获取Iterator功能 // Collect result back into an Object ...

    远程对象4.0版本

    1、大道至简,代码简化,去掉多余的封装和单元。 2、通过Http短连接的方式解决在线连接量的问题。 3、通过数据连接池优化并发查询的速度 4、实现数据集缓存机制,大幅优化大量相同查询的速度,且数据表变化后自动...

    Redis MongoDB 接口封装 C++

    1 开发了一个高效的C++对象池算法管理框架使用的对象 算法复杂度O 1 2 每个 MongoDB 的表对应一个 Module 通过配置 tmpl 文件可以自动生成表 Module 3 将 MongoDB 查询条件封装成装饰器 与表 Module 解耦合 4 开发...

    [java]读书笔记整理:一切都是对象

    一种通用的内存池(也位于RAM区),用于存放所有的java对象。堆不同于堆栈的好处是:编译器不需要知道要从堆里分配多少存储区域,也不必知道存储的数据在堆里存活多长时间。因此,在堆里分配存储有很大的灵活性。当...

    C++ 面向对象实践

    OOA、OOD与UML基础 Rose如何介入软件开发的全过程 案例:编写一个自己的简化版WinZip 案例:设计自己的事件驱动系统 案例:将缓冲池思想应用于数据集 案例:三层杂志社管理软件系统设计 159 页

    Mall4j商城系统 /JAVA商城系统 商城源码 仅限学习使用 如需商用购买商业版源码

    技术 版本 说明 Spring Boot 3.0.4 MVC核心框架 Spring Security web 3.0.4 web应用...lombok 1.18.26 简化对象封装工具 hutool 5.8.15 更适合国人的java工具集 knife4j 4.0.0 基于swagger,更便于国人使用的swagger ui

    基于SpringBoot+Layui实现电子商城系统源码+数据库+项目说明.zip

    Lombok 简化对象封装工具(需要安装IDEA插件) 【备注】 主要针对计算机相关专业的正在做毕设的学生和需要项目实战的Java学习者。 也可作为课程设计、期末大作业。包含:项目源码、数据库脚本、项目说明等,该项目...

    Java的String类讲解案例代码(String类使用:创建对象、比较、连接、截取、查找和替换、拆分...)StringBuf

    String类使用(创建字符串对象、比较、长度、连接、截取、查找和替换、切割和拆分、和其他类型的转换、格式化、判断、手动入池、其他操作) StringBuffer类 StringBuilder类 String、StringBuffer和StringBuilder的...

    面向对象技术的C++ OOD实践

    OOA、OOD与UML基础 Rose如何介入软件开发的全过程 案例:编写一个自己的简化版WinZip 案例:设计自己的事件驱动系统 案例:将缓冲池思想应用于数据集 案例:三层杂志社管理软件系统设计

    Fire使用说明

    Fire(火):是一个用于Java编程的数据库操作工具,轻巧简单实用,含数据库连接对象、数据库访问对象及代码生成三部分;由JSPGen软件开发框架第4.0版时提出(前身为JSPGen3.0时的数据库连接管理工具),是对传统JDBC...

    Spring HTTP连接池监控工具(高分项目).zip

    Java SSM项目是一种使用Java语言和SSM框架(Spring + Spring MVC + ...它提供了一种将数据库操作与Java对象映射起来的方式,避免了手动编写繁琐的SQL语句,并提供了事务管理和缓存等功能,简化了数据库访问的过程

    Common JDBC DAO组件系统库及帮助手册

    在使用CommonJDBCDAO组件时,只需要为CommonJDBCDAO组件类提供基于数据库连接池的DataSource对象或者应用CommonJDBCDAO组件自身所提供的数据库连接池的实现,然后通过CommonJDBCDAOFactory工厂类中相关的方法创建出...

    超级有影响力霸气的Java面试题大全文档

    wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。 17、...

    基于springboot+vue实现的前后端分离的智能图书管理系统,适合个人全栈web开发学习,用于java毕业设计,课程设计等

    具备增加、删除、修改、查找、借阅、还书、收藏的显示操作及实时数据库的提交和更改和对普通用户的增、删、改、查;...| Lombok | 简化对象封装工具 | | Hutool | Java工具类库 | | JWT | JWT登录支持

    practice-cs

    设计模式C# 游戏循环和更新方法-游戏循环在游戏过程中连续运行。... 对象池-定义一个维护可重用对象集合的池类。 当您需要一个新对象时,请向池中的一个对象发送请求。 完成对象处理后,将其返回到

Global site tag (gtag.js) - Google Analytics