Java中的对象池模式

Java中的对象池模式

Java对象的生命周期分析:

Java对象的生命周期大致包括三个阶段:

对象的创建,对象的使用, 对象的清除。 因此,对象的生命周期长度可用如下的表达式表示: T = T1 + T2 + T3.其中T1表示对象的创建时间,T2表示对象的使用时间,而T3则表示对象的清除时间。由此,我们可以看出,只有T2是真正有效的时间,而T1,T3则是对象本身的开销。下面再看看T1, T3在对象的整个生命周期中所占的比例。

我们知道,Java对象是通过构造函数来创建的,在这一过程中,该构造函数链中的所有构造函数也都会被自动调用。另外,默认情况下,调用类的构造函数时,Java会把变量初始化成确定的值:所有的对像被设置成null,整数变量(byte, short, int , long)设置成0,float和double变量设置成0.0,逻辑值设置成false,所以用new关键字来新建一个对象的时间开销是很大的,如表1所示。

表1 一些操作所消耗的时间的对照表

运算操作

示例

标准化时间

本地赋值

i = n

1.0

实例赋值

this.i = n

1.2

方法调用

Funct()

5.9

新建对象

New Object()

980

新建数组

New int[10]

3100

 

从表一可以看出,新建一个对象需要的980个单位的时间,是本地赋值时间的980倍,是方法调用时间的166倍,而若新建一个数组所花费的时间就更多了。

再看清除对象的过程。我们知道,Java语言的一个优势,就是Java程序员不需要像C/C++程序员那样,显式地释放对象,而由称为垃圾收集器(Garbage Colletor)的自动内存管理系统,定时或内存凸显不足时,自动回收垃圾对象所占的内存。凡事有利总也有弊,这虽然为Java程序员设计者提供了极大的方便,但同时它也带来了较大的性能开销。这种开销包括两方面:首先是对象管理的开销,GC为了能够正确释放对象,它必须监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等。其次,在GC开始回收”垃圾”对象时,系统会暂停应用程序的执行,而独占CPU.因此,如果要改善应用程序的性能,一方面应尽量减少创建对象的次数;同时,还应尽量减少T1,T3的时间。而这些均可以通过对象池技术来实现。

对象池技术的基本原理: 对象池技术基本原理的核心有两点:缓存和共享,即对于那些被频繁使用的对象,在使用完后,不立即将它们释放,而是将它们缓存起来,以供后续的应用程序重复使用,从而减少创建对象和释放对象的次数,进而改善应用程序的性能。事实上,由于对象池技术将对象限制在一定的数量,也有效地减少了应用程序在内存上的开销。

实现一个对象池,一般会涉及到如下的类:

⑴对象池工厂(ObjectPoolFactory)类

该类主要用于管理相同类型和设置的对象池(ObjectPool),它一般包含如下两个方法:createPool:用于创建特定类型和设置的对象池;destroyPool:用于释放指定的对象池;

同时为了保证ObjectPoolFactory的单一实例,可以采用Singleton设计模式,见下述getInstance方法的实现:

public synchronized ObjectPoolFactory getInstance() {

     if (poolFactory == null) {

         poolFactory = new ObjectPoolFactory();

         return poolFactory;

}

}

⑵参数对象(ParameterObject)类

       该类主要用于封装所创建对象池的一些属性参数,如池中可存放对象的数目的最大值(maxCount)、最小值(minCount)等。

对象池(ObjectPool)类

用于管理要被池化对象的借出和归还,并通知PoolableObjectFactory完成相应的工作。它一般包含如下两个方法:

◆   getObject: 用于从池中借出对象;

◆   returnObject:将池化对象返回到池中,并通知所有处于等待状态的线程;

◆   池化对象工厂(PoolableObjectFactory)类

该类主要负责管理池化对象的生命周期,就简单来说,一般包含对象的创建的创建和销毁。该类同ObjectPoolFactory一样,也可将其实现为单实例。

通用对象池的实现:

对象池的构造和治理可以按照多种方式实现。最灵活的方式是将池化对象的Class类型在对象池之外指定,即在ObjectPoolFactory类创建对象池时,动态指定该对象池所池化对象的Class类型,其实现代码如下:

public ObjectPool createPool(ParameterObject paraObj, Class clsType) {

             return new ObjectPool(paraObj, clsType);

}

其中,paraObj参数用于指定对象池对的特征属性,clsType参数则指定了该对象池所存放对象的类型。对象池(ObjectPool)创建以后,下面就是利用它来治理对象了,具体实现如下;

public class ObjectPool {

        private ParameteObject paraObj; //该对象池的属性参数对象

        private Class clsType; //该对象池中所存放对象得到类型

        private int currentNum = 0; //该对象池当前已创建的对象数目

        private Object currentObj; //该对象池当前可以

}

package com.test;

 

public class ObjectPoolFactory {

   

    /**

     * 饿汉式单例类

     */

    private static final ObjectPoolFactory factory = new ObjectPoolFactory();

   

    /**

     * 私有默认的构造子

     */

    private ObjectPoolFactory() {

      

    }

   

    /**

     * 静态工厂方法

    * @return  池工厂

     */

    public static ObjectPoolFactory getInstance() {

       return factory;

    }

   

    /**

     *

     * @param paraObj  对象池参数对象

     * @param clsType   所创建对象类型

     * @return   对象池

     */

    public ObjectPool createPool(ParameterObject paraObj, Class clsType) {

       return new ObjectPool(paraObj, clsType);

    }

 

}

package com.test;

 

import java.util.Vector;

 

public class ObjectPool {

 

    private ParameterObject paraObj// 该对象池的属性参数对象

 

    private Class clsType// 该对象池中所存放对象的类型

 

    private int currentNum = 0; // 该对象池当前已创建的对象数目

 

    private Object currentObj// 该对象池当前可以借出的对象

 

    private Vector pool// 用于存放对象的池

 

    public ObjectPool(ParameterObject paraObj, Class clsType) {

       this.paraObj = paraObj;

       this.clsType = clsType;

       pool = new Vector();

    }

 

    public Object getObject() {

       if (pool.size() <= paraObj.getMinCount()) {

           if (currentNum <= paraObj.getMaxCount()) { // 如果当前池中无对象可用,

                                                 // 而且已创建的对象数目小于所限制的最大值,

                                                 // 就利用PoolObjectFactory创建一个新的对象

              PoolableObjectFactory objFactory = PoolableObjectFactory

                     .getInstance();

              currentObj = objFactory.createObject(clsType);

              currentNum++;

           } else {

              // 如果当前池中无对象可用,而且所创建的对象数目已达到所限制的最大值,

              // 就只能等待其它线程返回对象到池中

              synchronized (this) {

                  try {

                     wait();

                  } catch (InterruptedException e) {

                     System.out.println(e.getMessage());

                     e.printStackTrace();

                  }

                  currentObj = pool.firstElement();

              }

           }

       } else {

           // 如果当前池中有可用的对象,就直接从池中取出对象

           currentObj = pool.firstElement();

       }

       return currentObj;

    }

 

    public void returnObject(Object obj) { // 确保对象具有正确的类型

       if (clsType.isInstance(obj)) {

           pool.addElement(obj);

           synchronized (this) {

              notifyAll();

           }

       } else {

 

       }

    }

 

}

package com.test;

 

public class PoolableObjectFactory {

   

    private static PoolableObjectFactory factory;

   

    private PoolableObjectFactory() {

      

    }

   

    public static synchronized PoolableObjectFactory getInstance() {

       if (factory == null) {

       factory = new PoolableObjectFactory();

       }

       return factory;

    }

 

    public Object createObject(Class clsType) {

       Object obj = null;

       try {

           obj = clsType.newInstance();

       } catch (InstantiationException e) {

           e.printStackTrace();

       } catch (IllegalAccessException e) {

           e.printStackTrace();

       }

       return obj;

    }

   

}

package com.test;

 

public class ParameterObject {

   

    private int MinCount;

   

    private int MaxCount;

   

    public ParameterObject(int MaxCount, int MinCount) {

       this.MaxCount = MaxCount;

       this.MinCount = MinCount;

    }

 

    public int getMinCount() {

       return MinCount;

    }

 

    public void setMinCount(int minCount) {

       MinCount = minCount;

    }

 

    public int getMaxCount() {

       return MaxCount;

    }

 

    public void setMaxCount(int maxCount) {

       MaxCount = maxCount;

    }

 

}

package com.test;

 

public class TestPool {

   

    public static void main(String[] args) {

       ObjectPoolFactory poolFactory = ObjectPoolFactory.getInstance();

       ParameterObject paraObj = new ParameterObject(2,1);

       ObjectPool pool = poolFactory.createPool(paraObj, StringBuffer.class);

       StringBuffer buffer = (StringBuffer)pool.getObject();

       buffer.append("hello");

       System.out.println(buffer.toString());

      

    }

 

}

 

 

String中的对象池模式:我们知道得到String对象有两种办法:String str1=”hello”;String str2=new String(“hello”);那么这两种创建String对象的方法有什么差异吗?当然有差异,差异在于第一种方法在对象池中拿对象,第二种方法直接生成新的对象。在JDK5.0里面,Java虚拟机在启动的时候会实例化9个对象池,这9个对象池分别用来存储8种基本类型的包装类对象和String对象。当我们在程序中直接用双引号括起来一个字符串是,JVM就到String的对象池里面去找看是否有一个值相同的对象,如果有,就拿现成的对象,如果没有就在对象池里创建一个对象,并返回。所以我们发现下面的代码输出true: String str1=”hello” ;String str2=”hello”;

System.out.println(str1==str2);这说明str1和str2指向同一个对象,因为它们都是在对象池中拿到的,而下面的代码输出为false;因为在任何情况下,只要你去new一个String对象那都是创建了新的对象。与此类似的,在JDK5.0里面8中基本类型的包装类也有这样的差异:Interger i1=5; 在对象池中拿到Integer i2=5;所以i1==i2 Integer i3= new Integer(5); //重新创建对象,所以i2 != i3对象池的存在是为了避免频繁的创建和销毁对象的而影响系统性能,那我们自己写的类是否也可以使用对象池呢?当然可以,考察下面的代码:

package com.test;

 

import java.util.HashSet;

 

import java.util.Set;

 

class Student {

   

    private int id;

   

    private String name;

   

    private static Set<Student> students = new HashSet<Student>();

 

    public Student(int id, String name) {

       this.id = id;

       this.name = name;

    }

   

    public static Student getInstance(int id, String name) {

      

       for (Student st : students) {

           if (id == st.id  && name.equals(st.name)) {

              return st;

           }

       }

       Student temp = new Student(id, name);

       students.add(temp);

       return temp;

      

    }

   

}

 

public class Test4 {

    public static void main(String[] args) {

       Student st1 = Student.getInstance(0, "liyangbing");

       Student st2 = Student.getInstance(0, "liyangbing");

       System.out.println("s1 == s2 is " + (st1 == st2)); //对象st2是从对象池中取得,故str1 == str2

       Student st3 = new Student(0, "liyangbing");

       Student st4 = new Student(0, "liyangbing");

       System.out.println("st3 == st4 is " + (st3 == st4)); //对象st3st4都是创建的,st3 != st4

    }

Java曾经号称是纯面向对象的语言,那么我们经常用的8中基本数据类型算不算对象呢?答案很肯定不算,那么java又怎能称得上是纯面向对像的语言呢?为此,SUN公司特意的推出了8中基本数据类型的包装类,这样java语言才算是称得上纯面向对象的语言,那么在JDK中就有9个对象池,8个基本类包装类对象和String类对象。请注意:在J2ME里面没有对象池的概念。

结束语:

恰当地使用对象池技术,能有效地改善应用程序的性能。目前,对象池技术已得到广泛的应用,如对于网络和数据库连接这类重量级的对象,一般都会采用对象池技术。但在使用对象池技术时也要注意如下问题:

并非任何情况下都适合采用对象池技术,基本上,只在重复生成某种对象的操作成为影响性能的关键因素的时候,才适合采用对象池技术。而如果进行池化所带来的性能提高并不重要的话,还是不采用对象池技术为佳,以保持代码的简明。

要根据具体情况正确选择对象池的实现方式。如果是创建一个公用的对象池技术实现包,或需要在程序中动态指定所池化对象的Class类型时,才选择通用对象池,而大部分情况下,采用专用对象池就可以了。

public class ObjectPool {
 private ParameterObject paraObj;//该对象池的属性参数对象
 private int currentNum = 0; //该对象池当前已创建的对象数目
 private StringBuffer currentObj;//该对象池当前可以借出的对象
 private Vector pool;//用于存放对象的池
 public ObjectPool(ParameterObject paraObj) {
  this.paraObj = paraObj;
  pool = new Vector();
 }
 public StringBuffer getObject() {
  if (pool.size() <= paraObj.getMinCount()) {
   if (currentNum <= paraObj.getMaxCount()) {
    currentObj = new StringBuffer();
    currentNum++;
   } 
   . . . 
  }
  return currentObj;
 }
 public void returnObject(Object obj) {
  // 确保对象具有正确的类型
  if (StringBuffer.isInstance(obj)) {
   . . . 
  }
 }

posted on 2013-10-08 18:02  Java码界探秘  阅读(173)  评论(0编辑  收藏  举报

导航