对象池的介绍与使用

1. 什么是对象池

对象池,顾名思义就是一定数量的已经创建好的对象(Object)的集合。当需要创建对象时,先在池子中获取,如果池子中没有符合条件的对象,再进行创建新对象,同样,当对象需要销毁时,不做真正的销毁,而是将其setActive(false),并存入池子中。这样就避免了大量对象的创建。

2. 对象池解决什么问题?

减少频繁创建和销毁对象带来的成本,实现对象的缓存和复用,创建对象的成本比较大,并且创建比较频繁。比如线程的创建代价比较大,于是就有了常用的线程 池。对象池(模式)是一种创建型设计模式,它持有一个初始化好的对象的集合,将对象提供给调用者。

一般而言对于 创建对象的成本比较大,并且创建比较频繁。比如线程的创建代价比较大,于是就有了常用的线程池。

3. 对象池的优缺点

3.1 对象池的优点:

  1. 提升了t获取对象的响应速度,比如单个线程和资源连接的创建成本都比较大。
  2. 运用对象池化技术可以显著地提升性能,尤其是当对象的初始化过程代价较大或者频率较高时。
  3. 一定程度上减少了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 方法解释:
GenericObjectPool 方法解释:

方法描述
borrowObject从池中借出一个对象。要么调用PooledObjectFactory.makeObject方法创建,要么对一个空闲对象使用PooledObjectFactory.activeObject进行激活,然后使用PooledObjectFactory.validateObject方法进行验证后再返回
returnObject将一个对象返还给池。根据约定:对象必须 是使用borrowObject方法从池中借出的
invalidateObject废弃一个对象。根据约定:对象必须 是使用borrowObject方法从池中借出的。通常在对象发生了异常或其他问题时使用此方法废弃它
addObject使用工厂创建一个对象,钝化并且将它放入空闲对象池
getNumberIdle返回池中空闲的对象数量。有可能是池中可供借出对象的近似值。如果这个信息无效,返回一个负数
getNumActive返回从借出的对象数量。如果这个信息不可用,返回一个负数
clear清除池中的所有空闲对象,释放其关联的资源(可选)。清除空闲对象必须使用PooledObjectFactory.destroyObject方法
close关闭池并释放关联的资源
  • 5
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Python进程池是一种并发编程技术,它允许我们同时运行多个进程来执行计算密集型或I/O密集型任务,从而提高程序的效率。下面我介绍一个简单的Python进程池案例。 假设我们有一个需要计算的函数`calculate(num)`,其中`num`是一个数字。我们需要计算`calculate()`函数在数字1到10之间所有数字的结果。我们可以使用Python进程池来加速这个过程。 首先,我们要导入`multiprocessing`模块,然后创建一个进程池对象。在这个例子中,我们使用`Pool()`函数来创建一个默认大小的进程池对象。 ```python import multiprocessing pool = multiprocessing.Pool() ``` 接下来,我们可以使用`map()`函数来并行计算每个数字的结果。`map()`函数将一个可迭代对象作为输入,对每个元素调用指定的函数,然后返回一个迭代器,其中包含每个函数的结果。 ```python results = pool.map(calculate, range(1, 11)) ``` 最后,我们可以打印每个数字的结果: ```python for result in results: print(result) ``` 完整的代码如下: ```python import multiprocessing def calculate(num): # 计算函数 return num * num if __name__ == '__main__': pool = multiprocessing.Pool() results = pool.map(calculate, range(1, 11)) for result in results: print(result) ``` 输出结果如下: ``` 1 4 9 16 25 36 49 64 81 100 ``` 该例子展示了如何使用Python进程池来提高程序的效率。我们可以根据需要调整进程池的大小,以达到最佳的性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dream_bin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值