当前位置: 首页 > news >正文

【设计模式】-创建型模式-第2章第5讲-【对象池模式】

目录

1、对象池模式的定义

1.1、先来看看百度百科的定义

1.2、 对象池模式就是单例模式加享元模式

2、为什么要用对象池模式

2.1、原因

2.2、解决方案

3、对象池模式示例代码

4、对象池模式的应用场景

 5、对象池模式的优缺点

5.1、优点

5.2、缺点

 6、结语


1、对象池模式的定义

1.1、先来看看百度百科的定义

对象池模式 (The Object Pool Pattern) 是单例模式的一个变种,它提供了获取一系列相同对象实例的入口。当你需要对象来代表一组可替代资源的时候就变的很有用,每个对象每次可以被一个组件使用。


1.2、 对象池模式就是单例模式加享元模式

对象池模式(Object Pool Pattern),是创建型设计模式的一种,将对象预先创建并初始化后放入对象池中,对象提供者就能利用已有的对象来处理请求,减少频繁创建对象所占用的内存空间和初始化时间。一个对象池包含一组已经初始化并且可以使用的对象,可以在有需求时创建和销毁对象。对象池的用户可以从池子中取得对象,对其进行操作处理,并在不需要时归还给池子而非直接销毁。对象池是一个特殊的工厂对象,对象池模式就是单例模式加享元模式

对象池模式和享元模式的最大区别在于:

对象池模式中会多一个回收对象重复利用的方法。所以,对象池模式应该是享元模式更加具体的一个应用场景。相当于先将对象从对象池中借出,用完之后再还回去,以此保证有限资源的重复利用。 

 了解了对象池模式的定义,接下来,咱们就该思考为什么要用对象池模式。 用对象池模式的好处是什么以及它的弊端。

2、为什么要用对象池模式

2.1、原因

我们都知道对象的实例化是最耗性能的操作之一,这在过去是个大问题,现在不用再过分关注它。但是当我们处理封装外部资源的对象(例如数据库连接)时,对象的创建操作则会耗费很多资源。

2.2、解决方案

 解决方案就是重用和共享这些创建成本高昂的对象,这就是我们讲到的对象池模式。先来看看它的类图结构,如下图 2-1。

 图 2-1 

对象池模式中使用的类如下所示:

  • ResourcePool (资源池类):用于封装逻辑的类。用来保存和管理资源列表。
  • Resource(资源类):用于封装特定资源的类。资源类通常被资源池类引用,因此只要资源池不重新分配,它们就永远不会被回收。
  • Client(客户端类):使用资源的类。

当客户需要新资源时,会向资源池类申请,资源池类检查后获取第一个可用资源并将其返回给客户端。

客户端使用完资源后会进行资源释放,资源会重新回到资源池以便重复使用。

3、对象池模式示例代码

ObjectPool.java 文件代码如下:

package com.zhaoyanfei.designpattern.objectPoolPattern;

import java.util.Enumeration;
import java.util.Vector;

/**
 * 对象池
 * @Date 2022年10月4日
 * @author zhaoYanFei
 *
 */
public class ObjectPool {

	private int numObjects = 10; // 对象池的大小 
	private int maxObjects = 50; // 对象池最大的大小 
	private Vector<PooledObject> objects = null; //存放对象池中对象的向量(PooledObject类型) 
	
	public ObjectPool() {
		
	}
	
	/*** 创建一个对象池***/ 
	public synchronized void createPool(){ 
		// 确保对象池没有创建。如果创建了,保存对象的向量 objects 不会为空 
		if (objects != null) { 
			return; // 如果己经创建,则返回 
		}
		// 创建保存对象的向量 , 初始时有 0 个元素 
		objects = new Vector<PooledObject>(); 
		// 根据 numObjects 中设置的值,循环创建指定数目的对象 
		for (int x = 0; x < numObjects; x++) {
			if ((objects.size() == 0)&&this.objects.size() <this.maxObjects) { 
				Object obj = new Object(); 
				objects.addElement(new PooledObject(obj)); 
			}
		}
	}
	/**
	 * 获取对象
	 * @return
	 */
	public synchronized Object getObject(){
		// 确保对象池己被创建 
		if (objects == null) { 
			return null; // 对象池还没创建,则返回 null 
		}
		Object conn = getFreeObject(); // 获得一个可用的对象 
		// 如果目前没有可以使用的对象,即所有的对象都在使用中 
		while (conn == null) {
			wait(250); 
			conn = getFreeObject(); // 重新再试,直到获得可用的对象,如果 
			// getFreeObject() 返回的为 null,则表明创建一批对象后也不可获得可用对象 
		} 
		return conn;// 返回获得的可用的对象 
	}
	/** 
	* 本函数从对象池对象 objects 中返回一个可用的的对象 
	* 如果当前没有可用的对象,则创建几个对象,并放入对象池中。 
	* 如果创建后,所有的对象都在使用中,则返回 null 
	*/ 
	private Object getFreeObject(){
		// 从对象池中获得一个可用的对象
		Object obj = findFreeObject();
		if (obj == null) {
			createPool();//如果目前对象池中没有可用的对象,创建一些对象 
			// 重新从池中查找是否有可用对象
			obj = findFreeObject();
			// 如果创建对象后仍获得不到可用的对象,则返回 null
			if (obj == null) {
				return null;
			}
		}
		return obj;
	}
	/** 
	* 查找对象池中所有的对象,查找一个可用的对象, 
	* 如果没有可用的对象,返回 null 
	*/ 
	private Object findFreeObject(){
		Object obj = null;
		PooledObject pObj = null;
		// 获得对象池向量中所有的对象 
		Enumeration enumerate = objects.elements();
		// 遍历所有的对象,看是否有可用的对象
		while (enumerate.hasMoreElements()) {
			pObj = (PooledObject) enumerate.nextElement(); 
			// 如果此对象不忙,则获得它的对象并把它设为忙 
			if (!pObj.isBusy()) { 
				obj = pObj.getObject(); 
				pObj.setBusy(true); 
			}
		}
		return obj;// 返回找到到的可用对象 
	}
	/** 
	* 此函数返回一个对象到对象池中,并把此对象置为空闲。 
	* 所有使用对象池获得的对象均应在不使用此对象时返回它。 
	*/ 
	public void returnObject(Object obj) { 
		// 确保对象池存在,如果对象没有创建(不存在),直接返回 
		if (objects == null) {
			return;
		}
		PooledObject pObj = null;
		Enumeration enumerate = objects.elements();
		// 遍历对象池中的所有对象,找到这个要返回的对象
		while (enumerate.hasMoreElements()) { 
			pObj = (PooledObject) enumerate.nextElement(); 
			// 先找到对象池中的要返回的对象
			if (obj == pObj.getObject()) {
				// 找到了 , 设置此对象为空闲状态
				pObj.setBusy(false); 
				break;
			} 
		}
	}
	/** 
	* 关闭对象池中所有的对象,并清空对象池。
	*/ 
	public synchronized void closeObjectPool() { 
		// 确保对象池存在,如果不存在,返回 
		if (objects == null) {
			return; 
		} 
		PooledObject pObj = null;
		Enumeration enumerate = objects.elements();
		while (enumerate.hasMoreElements()) {
			pObj = (PooledObject) enumerate.nextElement();
			// 如果忙,等 5 秒
			if (pObj.isBusy()) {
				wait(5000); // 等 5 秒 
			}
			// 从对象池向量中删除它 
			objects.removeElement(pObj); 
		}
		// 置对象池为空 
		objects = null; 
	}
	/** 
	* 使程序等待给定的毫秒数 
	*/ 
	private void wait(int mSeconds) { 
		try { 
			Thread.sleep(mSeconds); 
		} catch (InterruptedException e) {
			
		} 
	}
	/** 
	* 内部使用的用于保存对象池中对象的类。
	* 此类中有两个成员,一个是对象,另一个是指示此对象是否正在使用的标志 。
	*/ 
	class PooledObject {
		Object objection = null;// 对象
		boolean busy = false; // 此对象是否正在使用的标志,默认没有正在使用
		// 构造函数,根据一个 Object 构告一个 PooledObject 对象
		public PooledObject(Object objection) {
			this.objection = objection;
		}
		// 返回此对象中的对象 
		public Object getObject() {
			return objection;
		}
		// 设置此对象的,对象
		public void setObject(Object objection) {
			this.objection = objection; 
		} 
		// 获得对象对象是否忙
		public boolean isBusy() {
			return busy;
		}
		// 设置对象的对象正在忙
		public void setBusy(boolean busy) {
			this.busy = busy; 
		}
	}
	
}

ObjectPoolTest.java 对象池测试用例代码如下:

package com.zhaoyanfei.designpattern.objectPoolPattern;


/**
 * 对象池测试类
 * @Date 2022年10月4日
 * @author zhaoYanFei
 *
 */
public class ObjectPoolTest {

	public static void main(String[] args) {
		//创建对象池
		ObjectPool objPool = new ObjectPool();
		objPool.createPool();
		//客户端请求对象池获取对象Object
		Object obj = objPool.getObject();
		/**
		 * TODO
		 * 此处拿到对象做一些具体的业务操作
		 */
		//对象用完后归还到对象池中,供其他客户端请求复用对象
		objPool.returnObject(obj);
		//最后这个操作是关闭对象池中所有对象,并清空对象池
		objPool.closeObjectPool();
	}
	
}

4、对象池模式的应用场景

对象池模式主要适用于以下应用场景。

(1)资源受限的场景。比如,不需要可伸缩性的环境(CPU\内存等物理资源有限),CPU性能不够强劲,内存比较紧张,垃圾收集,内存抖动会造成比较大的影响,需要提高内存管理效率, 响应性比吞吐量更为重要。

(2)在内存中数量受限的对象。

(3)创建成本高的对象,可以考虑池化。

补充:常见的使用对象池的场景有在使用Socket时的各种连接池、线程池、数据库连接池等。

 5、对象池模式的优缺点

5.1、优点

复用池中对象,消除创建对象、回收对象所产生的内存开销、CPU开销,以及跨网络产生的网络开销。

5.2、缺点

(1)增加了分配 / 释放对象的开销。

(2)在并发环境中,多个线程可能(同时)需要获取池中对象,进而需要在堆数据结构上进行同步或者因为锁竞争而产生阻塞,这种开销要比创建销毁对象的开销高数百倍。

(3)由于池中对象的数量有限,势必成为一个可伸缩性瓶颈。

(4)很难合理设定对象池的大小,如果太小,则起不到作用;如果过大,则占用内存资源过高。

 6、结语

对象池模式的整体设计思想就是:

当客户需要新资源时,会向资源池类申请,资源池类检查后获取第一个可用资源并将其返回给客户端。

客户端使用完资源后会进行资源释放,资源会重新回到资源池以便重复使用。

码字不易,看完之后,感觉对您还有些帮助的话,那就动动你发财的小手, 帮小编点个赞,顺便再帮小编点个免费的关注呀,我会持续为您更新设计模式相关的内容。

你的点赞就是对小编最大的支持与认可。

如果对博文有所疑问或者指正,还望评论区留言哦^_^。

今天就到这儿吧,下期我会逐步详细讲解设计模式中的行为型模式。

下期咱们不见不散……

相关文章:

  • 125款浪漫七夕表白网站源码【建议收藏】HTML+CSS+JavaScript
  • 基于JAVA忻府区饭中有豆粮油销售系统计算机毕业设计源码+系统+数据库+lw文档+部署
  • 毕业设计 基于单片机的风速测量系统 - 物联网 嵌入式 stm32 arduino
  • 【MSP430G2553】图形化开发笔记(4) Timer_A 定时器
  • 【老板要我啥都会】|前端升全栈之项目使用express重构项目(上篇)
  • SpringMVC之使用SpringMVC获取参数与返回数据
  • (附源码)spring boot车辆管理系统 毕业设计 031034
  • 【Linux】关于Linux中的权限
  • 【FPGA教程案例92】图像处理1——基于FPGA的图像形态学膨胀处理实现,使用MATLAB辅助测试
  • 基于LabVIEW的温度计程序实现
  • 10.3 串口实验(A7核和M4核)
  • 使用Java实现一个定时器
  • 【CSS3】精灵图 背景图 阴影 过渡
  • 微服务笔记:第一章_微服务简介 | Eureka注册中心 | Nacos注册中心 | Nacos配置管理 | Feign
  • 信息检索经典文章-1
  • 07.Android之多媒体问题
  • C++回声服务器_9-epoll边缘触发模式版本服务器
  • ES6--对象的扩展
  • java8-模拟hadoop
  • JavaScript 基础知识 - 入门篇(一)
  • Javascript编码规范
  • JS学习笔记——闭包
  • Linux链接文件
  • MySQL常见的两种存储引擎:MyISAM与InnoDB的爱恨情仇
  • node-sass 安装卡在 node scripts/install.js 解决办法
  • PAT A1050
  • SpringCloud集成分布式事务LCN (一)
  • vagrant 添加本地 box 安装 laravel homestead
  • windows下如何用phpstorm同步测试服务器
  • 案例分享〡三拾众筹持续交付开发流程支撑创新业务
  • 理解在java “”i=i++;”所发生的事情
  • 目录与文件属性:编写ls
  • 排序(1):冒泡排序
  • 设计模式 开闭原则
  • 收藏好这篇,别再只说“数据劫持”了
  • 小程序开发中的那些坑
  • 移动端 h5开发相关内容总结(三)
  • 昨天1024程序员节,我故意写了个死循环~
  • ​ssh-keyscan命令--Linux命令应用大词典729个命令解读
  • ​云纳万物 · 数皆有言|2021 七牛云战略发布会启幕,邀您赴约
  • # centos7下FFmpeg环境部署记录
  • # 再次尝试 连接失败_无线WiFi无法连接到网络怎么办【解决方法】
  • #[Composer学习笔记]Part1:安装composer并通过composer创建一个项目
  • #LLM入门|Prompt#2.3_对查询任务进行分类|意图分析_Classification
  • (007)XHTML文档之标题——h1~h6
  • (3)(3.2) MAVLink2数据包签名(安全)
  • (C语言)深入理解指针2之野指针与传值与传址与assert断言
  • (delphi11最新学习资料) Object Pascal 学习笔记---第8章第5节(封闭类和Final方法)
  • ***通过什么方式***网吧
  • .Family_物联网
  • .NET 6 在已知拓扑路径的情况下使用 Dijkstra,A*算法搜索最短路径
  • .NET MVC、 WebAPI、 WebService【ws】、NVVM、WCF、Remoting
  • .NET(C#) Internals: as a developer, .net framework in my eyes
  • .NET6 开发一个检查某些状态持续多长时间的类
  • .NET中GET与SET的用法