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)); //对象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中的对象池模式的更多相关文章
- Java中的对象池技术
java中的对象池技术,是为了方便快捷地创建某些对象而出现的,当需要一个对象时,就可以从池中取一个出来(如果池中没有则创建一个),则在需要重复重复创建相等变量时节省了很多时间.对象池其实也就是一个内存 ...
- Java 中的对象池实现
点赞再看,动力无限.Hello world : ) 微信搜「程序猿阿朗 」. 本文 Github.com/niumoo/JavaNotes 和 未读代码博客 已经收录,有很多知识点和系列文章. 最近在 ...
- java设计模式之实现对象池模式示例分享
http://www.jb51.net/article/46941.htm 对象池模式经常用在频繁创建.销毁对象,且对象创建.销毁开销很大的场景,比如数据库连接池.线程池.任务队列池等.本代码简单,没 ...
- 【Java_基础】java中的常量池
1.java常量池的介绍 java中的常量池,通常指的是运行时常量池,它是方法区的一部分,一个jvm实例只有一个运行常量池,各线程间共享该运行常量池. java常量池简介:java常量池中保存了一份在 ...
- JAVA中JavaBean对象之间属性拷贝的方法
JAVA中JavaBean对象之间的拷贝通常是用get/set方法,但如果你有两个属性相同的JavaBean或有大部分属性相同的JavaBean,对于这种情况,可以采用以下几个简便方法处理. 下面对这 ...
- 【万字图文-原创】 | 学会Java中的线程池,这一篇也许就够了!
碎碎念 关于JDK源码相关的文章这已经是第四篇了,原创不易,粉丝从几十人到昨天的666人,真的很感谢之前帮我转发文章的一些朋友们. 从16年开始写技术文章,到现在博客园已经发表了222篇文章,大多数都 ...
- 《Java并发编程的艺术》 第9章 Java中的线程池
第9章 Java中的线程池 在开发过程中,合理地使用线程池能带来3个好处: 降低资源消耗.通过重复利用已创建的线程 降低线程创建和销毁造成的消耗. 提高响应速度.当任务到达时,任务可以不需要等到线程创 ...
- JAVA中创建线程池的五种方法及比较
之前写过JAVA中创建线程的三种方法及比较.这次来说说线程池. JAVA中创建线程池主要有两类方法,一类是通过Executors工厂类提供的方法,该类提供了4种不同的线程池可供使用.另一类是通过Thr ...
- GoLang设计模式06 - 对象池模式
这次介绍最后一个创建型模式--对象池模式.顾名思义,对象池模式就是预先初始化创建好多个对象,并将之保存在一个池子里.当需要的时候,客户端就可以从池子里申请一个对象使用,使用完以后再将之放回到池子里.池 ...
- 对象池模式(Object Pool Pattern)
本文节选自<设计模式就该这样学> 1 对象池模式的定义 对象池模式(Object Pool Pattern),是创建型设计模式的一种,将对象预先创建并初始化后放入对象池中,对象提供者就能利 ...
随机推荐
- Pandas从入门到放弃
公众号本文地址:https://mp.weixin.qq.com/s/mSkA5KvL1390Js8_1ZBiyw Pandas简介 Pandas是Panel data(面板数据)和Data anal ...
- 基于GitLab+Jenkin-CICD方案实践
前言 笔录于2022- 官网:https://about.gitlab.com/ 参考文档:https://docs.gitlab.com/ee/ci/ 清华源:清华大学开源软件镜像站 | Tsing ...
- PYRAFORMER: 用于长时间序列建模和预测的低复杂度金字塔注意力《PYRAFORMER: LOW-COMPLEXITY PYRAMIDAL ATTENTION FOR LONG-RANGE TIME SERIES MODELING AND FORECASTING》(金字塔注意力模块机制、PAM、CSCM、多尺度)
今天是2022年10月1日,今天重读一遍这篇论文. 10月1日16:48,上次读是4月20日,时间过得好快. 论文:PYRAFORMER: LOW-COMPLEXITY PYRAMIDAL ATTEN ...
- Yarn 3.0 Plug'n'Play (PnP) 安装和迁移
前言 以前用 npm, 后来 yarn 火了就用 yarn. 后来 yarn 2.0 大改版, Angular 不支持就一直没用. 一直到去年的 Angular 13 才开始支持. 最近又开始写 An ...
- Powershell 重新排列 Windows环境变量
最近乱搞环境变量,然后有些重复了,遂写个脚本去重下排序下. 环境变量有长度限制,如果超出了,比如SqlServer相关的,将共同路径单独搞个变量声明下,比如 将其路径手动替换成如下,可大幅压缩变量长度 ...
- Response状态码
1.数据是否正常 2.文件是否存在 3.地址自动跳转 4.服务提供错误 注:容错处理识别 •-1xx:指示信息-表示请求已接收,继续处理. •-2xx:成功-表示请求已经被成功接收.理解.接受. •- ...
- LeetCode 664. Strange Printer (DP)
题目: 有台奇怪的打印机有以下两个特殊要求: 打印机每次只能打印同一个字符序列.每次可以在任意起始和结束位置打印新字符,并且会覆盖掉原来已有的字符.给定一个只包含小写英文字母的字符串,你的任务是计算这 ...
- Uefi ABL读取XBL设置的标志位
PBL(启动固化程序)-> XBL(扩展引导加载程序,负责初始化芯片驱动和核心应用功能.XBL通常会加载一些平台相关的驱动程序,并提供通用接口)-> ABL(应用引导加载程序,负责引导操作 ...
- Java日期时间API系列14-----Jdk8中java.time包中的新的日期时间API类,java日期计算1,获取年月日时分秒等
通过Java日期时间API系列8-----Jdk8中java.time包中的新的日期时间API类的LocalDate源码分析 ,可以看出java8设计非常好,实现接口Temporal, Tempora ...
- 怎么根据token的有⽆去控制路由的跳转?进度条跳转 - 白名单是否有token - 单独封装文件permission .js
vue这边的路由⾃带了路由前置守卫,我们可以在前置守卫⾥拿到token数据,然后根据需求做分⽀判 断,要是token存在就使⽤next⽅法正常放⾏跳转,否则可以强制跳回到登录,让⽤户去获取token ...