池化技术(对象池)

什么是池化技术

池化技术是一种很常见的编程技巧,目的在于提前创建如内存,对象,线程资源,降低程序运行时频繁的创建销毁带来的开销。常见的有线程池,内存池,对象池等。

池化技术如何提高性能,降低系统开销

在实际的应用中,程序经常需要创建对象,线程和分配内存。这些设计到系统调用,系统调用会导致程序从用户态到内核态的切换会比较耗时。有了池化技术,需要用到对应的线程,对象和内存时不需要再切换上下文之需要从相应的池中获取,也不需要销毁,用完归还即可。

线程池实现的原理

先启动若干线程让其处于休眠状态/当用户中的系统需要访问时,从线程池中拿出一个已建立的空闲的连接,使用完毕后不关闭连接而是归还到线程池中。

数据库连接池的重要配置:最大连接数和最小连接数

线程池的重要配置:核心线程数,最大线程数

对象池

写一个对象池需要考虑的:

  • 对象池的结构 ,应该是维护两个固定大小的阻塞队列,两个队列一个是空闲一个使用。从空闲队列中获取对象后放入使用队列。使用完对象后不进行销毁而是放入空闲队列中 .
  • 池的大小 ,池中最大放多少,当池满后怎么解决 .
  • 池子怎么解决并发问题防止对一个对象的竞争

一 自定义实现对象池

示例
1 对象池接口

public interface HousePool {// 创建House getHouse();// 归还void release(House po);//初始化void init();
}

具体实现

@Service
public class HousePoolImpl implements HousePool{// 维护两个池子private LinkedBlockingQueue<House> idle ;private LinkedBlockingQueue<House> busy ;//维护池中的对象数量private final  int maxSize = 10 ;private AtomicInteger busySize = new AtomicInteger(0) ;@Overridepublic void init() {idle = new LinkedBlockingQueue<>();busy = new LinkedBlockingQueue<>();}@Overridepublic House getHouse() {// 创建对象池House po = null;po = idle.poll();// 有空闲对象 直接用if(po != null){busy.offer(po);return po;}// 未满 创建if(busySize.get() < maxSize){if(busySize.incrementAndGet() <= maxSize){System.out.println("创建House");po = new House();busy.offer(po);return po;}}// 已满等待或超时报错try {idle.poll(10000, TimeUnit.MILLISECONDS);if(po ==null){throw  new RuntimeException("超时");}busy.offer(po);} catch (InterruptedException e) {throw new RuntimeException(e);}return po;}@Overridepublic void release(House po) {busy.remove(po);idle.offer(po);}
}

初始化配置

@Configuration
public class PoolConfiguration {@Beanpublic  HousePool housePool(){HousePoolImpl housePool = new HousePoolImpl();housePool.init();return housePool;}
}

代码解读:
为什么使用LinkedBlockingQueue作为对象池化的容器
1 容量可选:可以选择指定容量,也可以不指定,不指定时容量为Integer.MAX_VALUE。
2 公平性选择:可以选择是否公平地对等待的生产者和消费者进行排序。
3 阻塞操作:当队列为空时,消费者会被阻塞直到队列非空;当队列满时,生产者会被阻塞直到队列有空间。
4 线程安全。

如何解决多线程环境下对同一个空闲对象的竞争

 private AtomicInteger busySize = new AtomicInteger(0) ;if(busySize.get() < maxSize){if(busySize.incrementAndGet() <= maxSize){System.out.println("创建House");po = new House();busy.offer(po);return po;}}

解决池满后等待问题

// 已满等待或超时报错try {idle.poll(10000, TimeUnit.MILLISECONDS);if(po ==null){throw  new RuntimeException("超时");}busy.offer(po);} catch (InterruptedException e) {throw new RuntimeException(e);}

测试

 @Testvoid testOrigin() {housePool.init();int i = 0 ;while(i < 10){House house = housePool.getHouse();System.out.println("得到hosue");housePool.release(house);i++;}}

结果在这里插入图片描述

可以看到对象只创建了一次,被使用了多次 。

二 使用commons-pool2实现对象池

commons-pool2是Apache Commons项目中的一个子项目,它提供了对象池化的实现,用于管理对象的创建、借出、归还和销毁等操作,以提高对象的重用率和系统性能。

commons-pool2主要包含以下特点:

  1. 通用性:可以用于池化各种类型的对象,如数据库连接、线程、Socket连接等。
  2. 配置灵活:可以通过配置参数来控制池的大小、对象的生命周期、借出和归还的行为等。
  3. 线程安全:提供了线程安全的对象池实现,适用于多线程环境。
  4. 监控和管理:提供了监控池状态、对象状态的功能,方便管理和调优。

通过使用commons-pool2,可以避免频繁地创建和销毁对象,提高系统的性能和资源利用率.

1 导入依赖

 <dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency>

2 实现PooledObjectFactory接口

public class HousePoolFactory implements PooledObjectFactory<House> {private static  int count = 1 ;@Overridepublic void activateObject(PooledObject<House> pooledObject) throws Exception {}@Overridepublic void destroyObject(PooledObject<House> pooledObject) throws Exception {}/*生成对象*/@Overridepublic PooledObject<House> makeObject() throws Exception {House house = new House(count++,"北京") ;System.out.println("创建一个对象"+house);return new DefaultPooledObject<>(house); // 封装成池化对象类型}/*** passivateObject(PooledObject<T> p):钝化对象,* 当对象归还到池中时调用,用于重置对象状态以便下次被借出时重新初始化* @throws Exception*/@Overridepublic void passivateObject(PooledObject<House> pooledObject) throws Exception {// 重置对象 .. 自定义重置方法pooledObject.getObject().init();}/*** 验证对象,用于检查对象是否仍然有效,如果对象无效则应该从池中移除。* @param pooledObject a {@code PooledObject} wrapping the instance to be validated** @return*/@Overridepublic boolean validateObject(PooledObject<House> pooledObject) {return true;}
}

接口方法说明

方法说明
activateObject(PooledObject p)激活对象,当对象从池中借出时调用,用于准备对象以便使用
destroyObject(PooledObject p)销毁对象,当对象从池中移除时调用,用于释放对象占用的资源
PooledObject makeObject():创建对象,当需要新的对象实例时调用,用于创建新的对象
passivateObject(PooledObject p)钝化对象,当对象归还到池中时调用,用于重置对象状态以便下次被借出时重新初始化
validateObject(PooledObject p)验证对象,用于检查对象是否仍然有效,如果对象无效则应该从池中移除

3 对象池的配置和使用

@Configuration
public class ObjectPoolConfig {public GenericObjectPool<House> genericObjectPool ;@PostConstructpublic void init() {System.out.println("初始化对象池...");HousePoolFactory factory = new HousePoolFactory();//设置对象池的相关参数GenericObjectPoolConfig poolConfig = initConfig();//新建一个对象池,传入对象工厂和配置genericObjectPool = new GenericObjectPool<>(factory, poolConfig);}/**\* 初始化配置*\* @param*/public GenericObjectPoolConfig initConfig() {GenericObjectPoolConfig cfg = new GenericObjectPoolConfig();cfg.setJmxNamePrefix("objectPool");//  对象总数cfg.setMaxTotal(10);// 最大空闲对象数cfg.setMaxIdle(2);// 最小空闲对象数cfg.setMinIdle(2);// 借对象阻塞最大等待时间// 获取资源的等待时间。blockWhenExhausted 为 true 时有效。-1 代表无时间限制,一直阻塞直到有可用的资源cfg.setMaxWaitMillis(1000l);// 最小驱逐空闲时间cfg.setMinEvictableIdleTimeMillis(200l);// 每次驱逐数量  资源回收线程执行一次回收操作,回收资源的数量。默认 3cfg.setNumTestsPerEvictionRun(3);// 回收资源线程的执行周期,默认 -1 表示不启用回收资源线程cfg.setTimeBetweenEvictionRunsMillis(100l);// 资源耗尽时,是否阻塞等待获取资源,默认 truecfg.setBlockWhenExhausted(true);return cfg;}
}

GenericObjectPool 类
GenericObjectPool 是一个通用对象池框架,我们可以借助它实现一个健壮的对象池,UML图如下所示:
在这里插入图片描述
在上面我设置了一个对象总数最大值为10并且最长等待时间为1s的对象池
测试 1: 每次都归还对象

@Testvoid testOrigin() throws Exception {GenericObjectPool<House> pool = objectPoolConfig.genericObjectPool;for (int i = 0; i < 10; i++) {House borrowObject = pool.borrowObject(); // 获取System.out.println(borrowObject);pool.returnObject(borrowObject); // 归还}}

请添加图片描述
测试2 :不归还对象 所需对象超过池中对象
结果:等待超时后报错
请添加图片描述

随便说说取对象的borrowObject()方法

代码看起来很复杂 简单来讲就是对象池维护了一个LinkedList,每次取的时候先从中拿到第一个对象,如果没有的话则进行创建,创建的create()方法实则是调用了我们重载后的PooledObjectFactory的makeObject方法。

while (p == null) {create = false;p = idleObjects.pollFirst(); // 尝试从池中获取对象if (p == null) {p = create(); // 创建对象 if (p != null) {create = true;}}if (blockWhenExhausted) { // 根据配置对资源进行等待if (p == null) {if (borrowMaxWaitDuration.isNegative()) {p = idleObjects.takeFirst();} else {p = idleObjects.pollFirst(borrowMaxWaitDuration);}}if (p == null) {throw new NoSuchElementException(appendStats("Timeout waiting for idle object, borrowMaxWaitDuration=" + borrowMaxWaitDuration));}} else if (p == null) {throw new NoSuchElementException(appendStats("Pool exhausted"));}if (!p.allocate()) {p = null;}if (p != null) {try {factory.activateObject(p); // 激活对象} catch (final Exception e) {try {destroy(p, DestroyMode.NORMAL); // 销毁对象 改变对象状态} catch (final Exception e1) {// Ignore - activation failure is more important}p = null;if (create) {final NoSuchElementException nsee = new NoSuchElementException(appendStats("Unable to activate object"));nsee.initCause(e);throw nsee;}}if (p != null && getTestOnBorrow()) {boolean validate = false;Throwable validationThrowable = null;try {validate = factory.validateObject(p); // 验证对象是否有效} catch (final Throwable t) {PoolUtils.checkRethrow(t);validationThrowable = t;}if (!validate) { // 对象无效 销毁对象try {destroy(p, DestroyMode.NORMAL);destroyedByBorrowValidationCount.incrementAndGet();} catch (final Exception e) {// Ignore - validation failure is more important}p = null;if (create) {final NoSuchElementException nsee = new NoSuchElementException(appendStats("Unable to validate object"));nsee.initCause(validationThrowable);throw nsee;}}}}}return p.getObject();

p = create()
核心代码

try {p = factory.makeObject();if (getTestOnCreate() && !factory.validateObject(p)) {createCount.decrementAndGet();return null;}} catch (final Throwable e) {createCount.decrementAndGet();throw e;} finally {synchronized (makeObjectCountLock) {makeObjectCount--;makeObjectCountLock.notifyAll();}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/247286.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

七、三台虚拟机JDK环境安装

目录 1、在三台机器上分别创建安装目录

glibc下的tpmalloc

文章目录 1、内存布局2、操作系统内存分配的相关函数2.1 Heap 操作相关函数2.2 Mmap 映射区域操作相关函数 3、ptmalloc的实现原理3.1 Main_arena 与 non_main_arena3.2 chunk 结构3.3 空闲 chunk 容器的组织形式3.3.1 small bin3.3.2 Large bins3.3.3 Unsorted bin3.3.4 Fast …

【Qt开发流程】之事件系统1:事件系统描述及事件发生流程

Qt的事件系统 在Qt中&#xff0c;事件是对象&#xff0c;派生自抽象的QEvent类&#xff0c;它表示应用程序内部发生的事情或作为应用程序需要知道的外部活动的结果。事件可以由QObject子类的任何实例接收和处理&#xff0c;但它们与小部件特别相关。以下描述了在典型应用程序中…

【南昌大学主办、往届均已实现EI、Scopus双检索】第三届电子信息工程与数据处理国际学术会议(EIEDP 2024)

第三届电子信息工程与数据处理国际学术会议&#xff08;EIEDP 2024&#xff09; 2024 3rd International Conference on Electronic Information Engineering and Data Processing 第三届电子信息工程与数据处理国际学术会议&#xff08;EIEDP 2024&#xff09;将于2024年3月1…

如何入驻抖音本地生活服务商,门槛太高怎么办?

随着抖音本地生活服务市场的逐渐成熟&#xff0c;越来越多平台开始涉及本地生活服务领域&#xff0c;而本地生活服务商成了一个香窝窝&#xff0c;为了保护用户权益和平台生态&#xff0c;对入驻入驻抖音本地生活服务商的条件及审核也越来越严格&#xff0c;这让很多想成为抖音…

为什么Nginx被称为反向代理

下图显示了 &#x1d41f;&#x1d428;&#x1d42b;&#x1d430;&#x1d41a;&#x1d42b;&#x1d41d; &#x1d429;&#x1d42b;&#x1d428;&#x1d431;&#x1d432; 和 &#x1d42b;&#x1d41e;&#x1d42f;&#x1d41e;&#x1d42b;&#x1d42c;&#…

JFrog----SBOM清单包含哪些:软件透明度的关键

文章目录 SBOM清单包含哪些&#xff1a;软件透明度的关键引言SBOM清单的重要性SBOM清单包含的核心内容SBOM的创建和管理结论 软件物料清单&#xff08;SBOM&#xff09;是一个在软件供应链安全中越来越重要的组成部分。它基本上是一份清单&#xff0c;详细列出了在特定软件产品…

51综合程序03-DS1302时钟

文章目录 DS1302时钟芯片一、DS1302时钟芯片的工作原理1. 芯片特点2. 引脚说明3. 寄存器地址4. 读数据的时序图5. 写数据的时序图 二、综合实例LCD1602显示 DS1302时钟芯片 一、DS1302时钟芯片的工作原理 1. 芯片特点 实时计算年、月、日、时、分、秒、星期&#xff0c;直到2…

java项目日常运维需要的文档资料

一、前言 java项目开发完成&#xff0c;部署上线&#xff0c;进入项目运维阶段&#xff0c;日常工作需要准备哪些资料和文档?当项目上线后&#xff0c;运行一段时间&#xff0c;或多或少会遇到一些运维上的问题&#xff0c;比如服务器磁盘饱满&#xff0c;服务器CPU&#xff0…

vue3 中使用 sse 最佳实践,封装工具

工具 // 接受参数 export interface SSEChatParams {url: string,// sse 连接onmessage: (event: MessageEvent) > void,// 处理消息的函数onopen: () > void,// 建立连接触发的事件finallyHandler: () > void,// 相当于 try_finally 中的 finally 部分&#xff0c;不…

ESP32-Web-Server编程-通过 Web 下载文本

ESP32-Web-Server编程-通过 Web 下载文本 概述 当你希望通过网页导出设备的数据时&#xff0c;可以在 ESP32 上部署一个简单的文件 Web 服务器。 需求及功能解析 本节演示如何在 ESP32 上部署一个最简单的 Web 服务器&#xff0c;来接收浏览器或者 wget 指令请求文件数据。…

layui+ssm实现数据批量删除

layuissm实现数据的批量删除 //数据表格table.render({id: adminList,elem: #adminList,url: ctx "/admin/getAdminList", //数据接口cellMinWidth: 80,even: true,toolbar: #toolbarDemo,//头部工具栏limit: 10,//每页条数limits: [10, 20, 30, 40],defaultToolba…