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)); //对象st3和st4都是创建的,故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)) {
   . . . 
  }
 }

Java中的对象池模式的更多相关文章

  1. Java中的对象池技术

    java中的对象池技术,是为了方便快捷地创建某些对象而出现的,当需要一个对象时,就可以从池中取一个出来(如果池中没有则创建一个),则在需要重复重复创建相等变量时节省了很多时间.对象池其实也就是一个内存 ...

  2. Java 中的对象池实现

    点赞再看,动力无限.Hello world : ) 微信搜「程序猿阿朗 」. 本文 Github.com/niumoo/JavaNotes 和 未读代码博客 已经收录,有很多知识点和系列文章. 最近在 ...

  3. java设计模式之实现对象池模式示例分享

    http://www.jb51.net/article/46941.htm 对象池模式经常用在频繁创建.销毁对象,且对象创建.销毁开销很大的场景,比如数据库连接池.线程池.任务队列池等.本代码简单,没 ...

  4. 【Java_基础】java中的常量池

    1.java常量池的介绍 java中的常量池,通常指的是运行时常量池,它是方法区的一部分,一个jvm实例只有一个运行常量池,各线程间共享该运行常量池. java常量池简介:java常量池中保存了一份在 ...

  5. JAVA中JavaBean对象之间属性拷贝的方法

    JAVA中JavaBean对象之间的拷贝通常是用get/set方法,但如果你有两个属性相同的JavaBean或有大部分属性相同的JavaBean,对于这种情况,可以采用以下几个简便方法处理. 下面对这 ...

  6. 【万字图文-原创】 | 学会Java中的线程池,这一篇也许就够了!

    碎碎念 关于JDK源码相关的文章这已经是第四篇了,原创不易,粉丝从几十人到昨天的666人,真的很感谢之前帮我转发文章的一些朋友们. 从16年开始写技术文章,到现在博客园已经发表了222篇文章,大多数都 ...

  7. 《Java并发编程的艺术》 第9章 Java中的线程池

    第9章 Java中的线程池 在开发过程中,合理地使用线程池能带来3个好处: 降低资源消耗.通过重复利用已创建的线程 降低线程创建和销毁造成的消耗. 提高响应速度.当任务到达时,任务可以不需要等到线程创 ...

  8. JAVA中创建线程池的五种方法及比较

    之前写过JAVA中创建线程的三种方法及比较.这次来说说线程池. JAVA中创建线程池主要有两类方法,一类是通过Executors工厂类提供的方法,该类提供了4种不同的线程池可供使用.另一类是通过Thr ...

  9. GoLang设计模式06 - 对象池模式

    这次介绍最后一个创建型模式--对象池模式.顾名思义,对象池模式就是预先初始化创建好多个对象,并将之保存在一个池子里.当需要的时候,客户端就可以从池子里申请一个对象使用,使用完以后再将之放回到池子里.池 ...

  10. 对象池模式(Object Pool Pattern)

    本文节选自<设计模式就该这样学> 1 对象池模式的定义 对象池模式(Object Pool Pattern),是创建型设计模式的一种,将对象预先创建并初始化后放入对象池中,对象提供者就能利 ...

随机推荐

  1. React函数式组件避免无用渲染的方案

    在class组件中可以使用shouldComponentUpdate钩子函数,但是函数式组件中是没有这种钩子函数的,那么在函数式组件中来达到类似的效果呢? 答案是:React.Memo,如以下使用案例 ...

  2. CSS – PostCSS

    前言 我第一次接触 PostCSS 是在学 Tailwind CSS 的时候. 它类似 JavaScript 的 Babel. 我没有用过 Babel, 因为 TypeScript 用的早. Post ...

  3. Nodejs+npm详细安装

    Nodejs详细安装步骤1.去官网下载nodejs,下载地址:https://nodejs.org/en/download/ 根据自己要求下载,我这里下载的是windows installer的. 2 ...

  4. MoNA:复用跨模态预训练模型,少样本模态的福音 | ICML'24

    跨模态转移旨在利用大型预训练模型来完成可能不属于预训练数据模态的任务.现有的研究在将经典微调扩展到跨模态场景方面取得了一定的成功,但仍然缺乏对模态差距对转移的影响的理解.在这项工作中,进行了一系列关于 ...

  5. 前端VUE调用后台接口,实现基本增删改查

    设置接口请求 作为一个后台,个人一点感想:前端现在都是组件化开发,会看文档基本功能就能实现. js文件 import request from '@/router/axios' // 查询 expor ...

  6. transaction_timeout:达到事务超时时终止会话

    功能实现背景说明 我们已经有两个参数来控制长事务:statement_timeout 和 idle_in_transaction_session_timeout.但是,如果事务执行的命令足够短且不超过 ...

  7. 元素偏移量offset 与 style 区别 ?

    offset 可以得到任意样式表中的样式值offset 系列获得的数值是没有单位的数字offsetWidth 包含padding+border+width offsetWidth 等属性是只读属性,只 ...

  8. 什么是 vite ?

    vite 是尤雨溪团队开发的新一代前端构建工具,作者尤雨溪宣传的时候表示 vite 是下一代的构建工具,并表示自己再也不会 webpack 了 : vite 的优点主要有3点 : 第一:开发环境中,无 ...

  9. 亿图图示最新版本(Win和Mac)适用的最高45天免费会员,Edrawmax,MindMaster,windows和mac兼用免费会员

    活动随时都在变化,感兴趣的快点去领吧! 亿图图示:https://www.edrawsoft.cn/viral-marketing/Invited.html?s_uid=26349406&pr ...

  10. tekton初次安装报错“containers with incomplete status: [place-tools]”

    报错内容 在按照官方部署方式部署完毕以后,执行第一个taskrun的时候就报错了,报错如下 Status: Conditions: Last Transition Time: 2022-08-08T0 ...