详解Java中对象池的介绍与使用
1. 什么是对象池
对象池,顾名思义就是一定数量的已经创建好的对象(Object)的集合。当需要创建对象时,先在池子中获取,如果池子中没有符合条件的对象,再进行创建新对象,同样,当对象需要销毁时,不做真正的销毁,而是将其setActive(false),并存入池子中。这样就避免了大量对象的创建。
2. 对象池解决什么问题
减少频繁创建和销毁对象带来的成本,实现对象的缓存和复用,创建对象的成本比较大,并且创建比较频繁。比如线程的创建代价比较大,于是就有了常用的线程 池。对象池(模式)是一种创建型设计模式,它持有一个初始化好的对象的集合,将对象提供给调用者。
一般而言对于 创建对象的成本比较大,并且创建比较频繁。比如线程的创建代价比较大,于是就有了常用的线程池。
3. 对象池的优缺点
3.1 对象池的优点
提升了t获取对象的响应速度,比如单个线程和资源连接的创建成本都比较大。
运用对象池化技术可以显著地提升性能,尤其是当对象的初始化过程代价较大或者频率较高时。
一定程度上减少了GC的压力。对于实时性要求较高的程序有很大的帮助
比如说 http 链接的对象池,Redis对象池等等都使用了对象池
3.2 对象池弊端
1.脏对象的问题
所谓的脏对象就是指的是当对象被放回对象池后,还保留着刚刚被客户端调用时生成的数据。
脏对象可能带来两个问题:
1).脏对象持有上次使用的引用,导致内存泄漏等问题。
2). 脏对象如果下一次使用时没有做清理,可能影响程序的处理数据。
2.生命周期的问题
处于对象池中的对象生命周期要比普通的对象要长久。维持大量的对象也是比较占用内存空间的。
4. 对象池有什么特征
一般来说,对象池有下面几个特征:
(1)对象池中有一定数量已经创建好的对象
(2)对象池向用户提供获取对象的接口,当用户需要新的对象时,便可通过调用此接口获取新的对象。如果对象池中有事先创建好的对象时,就直接返回给用 户;如果没有了,对象池还可以创建新的对象加入其中,然后返回给用户
(3)对象池向用户提供归还对象的接口,当用户不再使用某对象时,便可通过此接口把该对象归还给对象池
5. 池的大小选择
通常情况下,我们需要控制对象池的大小如果对象池没有限制,可能导致对象池持有过多的闲置对象,增加内存的占用。如果对象池闲置过小,没有可用的对象时,会造成之前对象池无可用的对象时,再次请求出现的问题。
对象池的大小选取应该结合具体的使用场景,结合数据(触发池中无可用对象的频率)分析来确定。现在Java的对象分配操作不比c语言的malloc调用慢, 对于轻中量级的对象, 分配/释放对象的开销可以忽略不计,并发环境中, 多个线程可能(同时)需要获取池中对象, 进而需要在堆数据结构上进行同步或者因为锁竞争而产生阻塞, 这种开销要比创建销毁对象的开销高数百倍;由于池中对象的数量有限, 势必成为一个可伸缩性瓶颈;很难正确的设定对象池的大小, 如果太小则起不到作用, 如果过大, 则占用内存资源高。
空间换时间的折中,本质上,对象池属于空间换时间的折中。它通过缓存初始化好的对象来提升调用者请求对象的响应速度。除此之外,折中(tradeoff)是软件开发中的一个重要的概念,会贯穿整个软件开发过程中。
6. 对象池的使用
6.1 接入
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency>
6.2 实现线程池工厂
import com.scl.online.service.model.SxInferContext; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.PooledObjectFactory; import org.apache.commons.pool2.impl.DefaultPooledObject; /** * 实现PooledObjectFactory * * @author : cuilinsu * @since : 2021/4/14 17:56 */ public class InferContextPooledObjectFactory implements PooledObjectFactory<SxInferContext> { @Override public PooledObject<SxInferContext> makeObject() { SxInferContext inferContext = new SxInferContext(); return new DefaultPooledObject<>(inferContext); } @Override public void destroyObject(PooledObject<SxInferContext> pooledObject) { } @Override public boolean validateObject(PooledObject<SxInferContext> pooledObject) { return true; } @Override public void activateObject(PooledObject<SxInferContext> pooledObject) { pooledObject.getObject().initObject(); } @Override public void passivateObject(PooledObject<SxInferContext> pooledObject) { // 当ObjectPool实例返还池中的时候调用 pooledObject.getObject().initObject(); } }
说明:
1.SxInferContext:为对象池里头的对象,对象借还都会调用到PooledObjectFactory里头的方法
2.PooledObjectFactory负责管理PooledObject,如:借出对象,返回对象,校验对象,有多少激活对象,有多少空闲对象。
方法 | 描述 |
---|---|
makeObject | 用于生成一个新的ObjectPool实例 |
activateObject | 每一个钝化(passivated)的ObjectPool实例从池中借出(borrowed)前调用 |
validateObject | 可能用于从池中借出对象时,对处于激活(activated)状态的ObjectPool实例进行测试确保它是有效的。也有可能在ObjectPool实例返还池中进行钝化前调用进行测试是否有效。它只对处于激活状态的实例调用 |
passivateObject | 当ObjectPool实例返还池中的时候调用 |
destroyObject | 当ObjectPool实例从池中被清理出去丢弃的时候调用(是否根据validateObject的测试结果由具体的实现在而定) |
6.3 初始化
public GenericObjectPool<SxInferContext> contextPools; @PostConstruct public void init() { if (sxInferConfig.isObjectPoolUsable()) { InferContextPooledObjectFactory factory = new InferContextPooledObjectFactory(); //设置对象池的相关参数 GenericObjectPoolConfig poolConfig = initConfig(); //新建一个对象池,传入对象工厂和配置 contextPools = new GenericObjectPool<>(factory, poolConfig); } } /** \* 池子初始化 * \* @param */ public GenericObjectPoolConfig initConfig() { GenericObjectPoolConfig cfg = new GenericObjectPoolConfig(); cfg.setJmxNamePrefix("objectPool"); // 对象总数 cfg.setMaxTotal(sxInferConfig.getPoolMaxTotal()); // 最大空闲对象数 cfg.setMaxIdle(sxInferConfig.getPoolMaxIdle()); // 最小空闲对象数 cfg.setMinIdle(sxInferConfig.getPoolMinIdle()); // 借对象阻塞最大等待时间 // 获取资源的等待时间。blockWhenExhausted 为 true 时有效。-1 代表无时间限制,一直阻塞直到有可用的资源 cfg.setMaxWaitMillis(sxInferConfig.getPoolMaxWait()); // 最小驱逐空闲时间 cfg.setMinEvictableIdleTimeMillis(sxInferConfig.getPoolMinEvictableIdleTimeMillis()); // 每次驱逐数量 资源回收线程执行一次回收操作,回收资源的数量。默认 3 cfg.setNumTestsPerEvictionRun(sxInferConfig.getPoolNumTestsPerEvictionRun()); // 回收资源线程的执行周期,默认 -1 表示不启用回收资源线程 cfg.setTimeBetweenEvictionRunsMillis(sxInferConfig.getPoolTimeBetweenEvictionRunsMillis()); // 资源耗尽时,是否阻塞等待获取资源,默认 true cfg.setBlockWhenExhausted(sxInferConfig.isPoolBlockWhenExhausted()); return cfg; }
6.4 使用
contextPools.borrowObject();
contextPools.returnObject();
等等 ....
说明:cfg.setJmxNamePrefix(“objectPool”); 假如项目中有用到redis线程池,则需要配置一下JmxNamePrefix。redis线程池使用的是“pool”,假如有重复的,早调用线程池是时,就默认会调用到Redis线程池的PooledObjectFactory(假如redis线程池使用默认的话),导致配置的线程池不生效。
GenericObjectPool 方法解释:
方法 | 描述 |
---|---|
borrowObject | 从池中借出一个对象。要么调用PooledObjectFactory.makeObject方法创建,要么对一个空闲对象使用PooledObjectFactory.activeObject进行激活,然后使用PooledObjectFactory.validateObject方法进行验证后再返回 |
returnObject | 将一个对象返还给池。根据约定:对象必须 是使用borrowObject方法从池中借出的 |
invalidateObject | 废弃一个对象。根据约定:对象必须 是使用borrowObject方法从池中借出的。通常在对象发生了异常或其他问题时使用此方法废弃它 |
addObject | 使用工厂创建一个对象,钝化并且将它放入空闲对象池 |
getNumberIdle | 返回池中空闲的对象数量。有可能是池中可供借出对象的近似值。如果这个信息无效,返回一个负数 |
getNumActive | 返回从借出的对象数量。如果这个信息不可用,返回一个负数 |
clear | 清除池中的所有空闲对象,释放其关联的资源(可选)。清除空闲对象必须使用PooledObjectFactory.destroyObject方法 |
close | 关闭池并释放关联的资源 |
到此这篇关于详解Java中对象池的介绍与使用的文章就介绍到这了,更多相关Java对象池内容请搜索猪先飞以前的文章或继续浏览下面的相关文章希望大家以后多多支持猪先飞!
原文出处:https://blog.csdn.net/Dream_bin/article/details/116242953
相关文章
- 这篇文章主要介绍了如何利用java语言实现经典《复杂迷宫》游戏,文中采用了swing技术进行了界面化处理,感兴趣的小伙伴可以动手试一试...2022-02-01
java 运行报错has been compiled by a more recent version of the Java Runtime
java 运行报错has been compiled by a more recent version of the Java Runtime (class file version 54.0)...2021-04-01- 这篇文章主要介绍了在java中获取List集合中最大的日期时间操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-15
- 这篇文章主要介绍了教你怎么用Java获取国家法定节假日,文中有非常详细的代码示例,对正在学习java的小伙伴们有非常好的帮助,需要的朋友可以参考下...2021-04-23
- 这篇文章主要介绍了Java如何发起http请求的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-03-31
- 说起C#和Java这两门语言(语法,数据类型 等),个人以为,大概有90%以上的相似,甚至可以认为几乎一样。但是在工作中,我也发现了一些细微的差别...2020-06-25
- 这篇文章主要介绍了解决Java处理HTTP请求超时的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-03-29
- 这篇文章主要介绍了java 判断两个时间段是否重叠的案例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-15
java 画pdf用itext调整表格宽度、自定义各个列宽的方法
这篇文章主要介绍了java 画pdf用itext调整表格宽度、自定义各个列宽的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-01-31- 这篇文章主要介绍了超简洁java实现双色球若干注随机号码生成(实例代码),本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-04-02
- 这篇文章主要介绍了Java生成随机姓名、性别和年龄的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-10-01
- 这篇文章主要介绍了java正则表达式判断前端参数修改表中另一个字段的值,需要的朋友可以参考下...2021-05-07
Java使用ScriptEngine动态执行代码(附Java几种动态执行代码比较)
这篇文章主要介绍了Java使用ScriptEngine动态执行代码,并且分享Java几种动态执行代码比较,需要的朋友可以参考下...2021-04-15- 这篇文章主要介绍了Java开发实现人机猜拳游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-08-03
Java 8 Stream 的终极技巧——Collectors 功能与操作方法详解
这篇文章主要介绍了Java 8 Stream Collectors 功能与操作方法,结合实例形式详细分析了Java 8 Stream Collectors 功能、操作方法及相关注意事项,需要的朋友可以参考下...2020-05-20- 这篇文章主要介绍了Java List集合返回值去掉中括号('[ ]')的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-29
Java中lombok的@Builder注解的解析与简单使用详解
这篇文章主要介绍了Java中lombok的@Builder注解的解析与简单使用,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-01-06- 下面小编就为大家带来一篇java中String类型变量的赋值问题介绍。小编觉得挺不错的。现在分享给大家,给大家一个参考。...2016-03-28
- 这篇文章主要介绍了Java线程池中的各个参数如何合理设置操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-06-19
- 在Java中,我们可以利用多线程来最大化地压榨CPU多核计算的能力,下面这篇文章主要给大家介绍了关于java中多线程与线程池基本使用的相关资料,需要的朋友可以参考下...2021-09-13