池化技术(对象池)

本文介绍了池化技术,包括线程池、内存池和对象池的概念,以及它们如何通过减少系统开销和提高性能。详细讲解了线程池的工作原理和重要配置,以及使用ApacheCommonsPool2实现对象池的示例和优化策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是池化技术

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

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

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

线程池实现的原理

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

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

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

对象池

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

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

一 自定义实现对象池

示例
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) ;

    @Override
    public void init() {
         idle = new LinkedBlockingQueue<>();
         busy = new LinkedBlockingQueue<>();
    }

    @Override
    public 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;
    }

    @Override
    public void release(House po) {
        busy.remove(po);
        idle.offer(po);
    }
}

初始化配置

@Configuration
public class PoolConfiguration {

    @Bean
    public  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);
        }

测试

 @Test
    void 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 ;
    @Override
    public void activateObject(PooledObject<House> pooledObject) throws Exception {

    }

    @Override
    public void destroyObject(PooledObject<House> pooledObject) throws Exception {

    }

    /*
    生成对象
     */
    @Override
    public PooledObject<House> makeObject() throws Exception {
        House house = new House(count++,"北京") ;
        System.out.println("创建一个对象"+house);
        return new DefaultPooledObject<>(house); // 封装成池化对象类型
    }

    /**
     * passivateObject(PooledObject<T> p):钝化对象,
     * 当对象归还到池中时调用,用于重置对象状态以便下次被借出时重新初始化
     * @throws Exception
     */
    @Override
    public void passivateObject(PooledObject<House> pooledObject) throws Exception {
       // 重置对象 .. 自定义重置方法
        pooledObject.getObject().init();
    }

    /**
     * 验证对象,用于检查对象是否仍然有效,如果对象无效则应该从池中移除。
     * @param pooledObject a {@code PooledObject} wrapping the instance to be validated
     *
     * @return
     */
    @Override
    public 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 ;

    @PostConstruct
    public 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);
        // 每次驱逐数量  资源回收线程执行一次回收操作,回收资源的数量。默认 3
        cfg.setNumTestsPerEvictionRun(3);
        // 回收资源线程的执行周期,默认 -1 表示不启用回收资源线程
        cfg.setTimeBetweenEvictionRunsMillis(100l);
        // 资源耗尽时,是否阻塞等待获取资源,默认 true
        cfg.setBlockWhenExhausted(true);
        return cfg;
    }
}

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

@Test
    void 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();
            }
        }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值