什么是池化技术
池化技术是一种很常见的编程技巧,目的在于提前创建如内存,对象,线程资源,降低程序运行时频繁的创建销毁带来的开销。常见的有线程池,内存池,对象池等。
池化技术如何提高性能,降低系统开销
在实际的应用中,程序经常需要创建对象,线程和分配内存。这些设计到系统调用,系统调用会导致程序从用户态到内核态的切换会比较耗时。有了池化技术,需要用到对应的线程,对象和内存时不需要再切换上下文之需要从相应的池中获取,也不需要销毁,用完归还即可。
线程池实现的原理
先启动若干线程让其处于休眠状态/当用户中的系统需要访问时,从线程池中拿出一个已建立的空闲的连接,使用完毕后不关闭连接而是归还到线程池中。
数据库连接池的重要配置:最大连接数和最小连接数
线程池的重要配置:核心线程数,最大线程数
对象池
写一个对象池需要考虑的:
- 对象池的结构 ,应该是维护两个固定大小的阻塞队列,两个队列一个是空闲一个使用。从空闲队列中获取对象后放入使用队列。使用完对象后不进行销毁而是放入空闲队列中 .
- 池的大小 ,池中最大放多少,当池满后怎么解决 .
- 池子怎么解决并发问题防止对一个对象的竞争
一 自定义实现对象池
示例
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主要包含以下特点:
- 通用性:可以用于池化各种类型的对象,如数据库连接、线程、Socket连接等。
- 配置灵活:可以通过配置参数来控制池的大小、对象的生命周期、借出和归还的行为等。
- 线程安全:提供了线程安全的对象池实现,适用于多线程环境。
- 监控和管理:提供了监控池状态、对象状态的功能,方便管理和调优。
通过使用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();}}