apache.commons.pool2 使用指南
为什么要使用池
创建对象耗时较长,多线程频繁调用等因素限制了我们不能每次使用时都重新创建对象,使用池化思想将对象放进池内,不同线程使用同一个池来获取对象,极大的减少每次业务的调用时间。
一个线程内多次使用这个对象所建立的连接。
使用场景
- 数据库连接池:HikariCP、Druid
- 自定义业务连接池:耗时的客户端连接远程服务端
如何使用
1、创建对象客户端
java
package com.ncst.client.mcc;
import lombok.extern.slf4j.Slf4j;
import java.util.Objects;
/**
* @Author: Lisy
* @Description: MCC 操作工具类
*/
@Slf4j
public class MccClient {
public MccClient() {
}
public void putMc(String key, String value) {
if (Objects.isNull(key)) {
throw new IllegalArgumentException("The key argument cannot be null");
}
log.info("{}=={}", key, value);
}
public String getMc(String key) {
if (Objects.isNull(key)) {
throw new IllegalArgumentException("The " + key + " argument cannot be null");
}
return "";
}
}
2、创建池工厂类
java
package com.ncst.client.mcc;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.DefaultPooledObject;
/**
* @Author: Lisy
* @Description: 对象池工厂
*/
public class MccClientFactory implements PooledObjectFactory<MccClient> {
public MccClientFactory() {
}
@Override
public PooledObject<MccClient> makeObject() {
MccClient dccClient = new MccClient();
return new DefaultPooledObject<>(dccClient);
}
@Override
public void destroyObject(PooledObject<MccClient> p) {
MccClient object = p.getObject();
object.close();
}
@Override
public boolean validateObject(PooledObject<MccClient> p) {
// 验证对象是否可用的逻辑,如果需要
return true;
}
@Override
public void activateObject(PooledObject<MccClient> p) {
// 激活对象的逻辑,如果需要
}
@Override
public void passivateObject(PooledObject<MccClient> p) {
// 钝化对象的逻辑,如果需要
}
}
3、对象池管理类
用于管理保存的池对象,单例模式
shutdown 方法在不使用时关闭掉对应任务的所有池连接
java
package com.ncst.client;
import com.ncst.client.mcc.MccClient;
import com.ncst.client.mcc.MccClientFactory;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/**
* @Author: Lisy
* @Description: 对象池管理
*/
public class MccClientPoolManager {
private static volatile MccClientPoolManager INSTANCE;
private MccClientPoolManager() {
}
public static MccClientPoolManager getInstance() {
if (Objects.isNull(INSTANCE)) {
synchronized (MccClientPoolManager.class) {
if (Objects.isNull(INSTANCE)) {
INSTANCE = new MccClientPoolManager();
}
}
}
return INSTANCE;
}
/**
* key-jobUuid, value-每一个任务池
*/
private final ConcurrentHashMap<String, GenericObjectPool<MccClient>> POOL_MAP = new ConcurrentHashMap<>();
public GenericObjectPool<MccClient> getPool(String jobId, GenericObjectPoolConfig<MccClient> poolConfig) {
return POOL_MAP.computeIfAbsent(jobId, k -> new GenericObjectPool<>(new MccClientFactory(), poolConfig));
}
public void shutdown(String jobId) {
GenericObjectPool<MccClient> dccClientGenericObjectPool = POOL_MAP.get(jobId);
if (Objects.nonNull(dccClientGenericObjectPool)) {
dccClientGenericObjectPool.close();
}
}
}
4、使用
通过多线程来模拟实际使用场景,如果使用默认poolConfig,那么最大池大小为8个,最多创建8个对象。
通过CountDownLatch 来等待所有任务完成
通过 MccClientPoolManager.getInstance().shutdown("key"); 来关闭池
"key" 可根据业务逻辑自行定义
java
public static void main(String[] args) throws InterruptedException {
int num = 20;
CountDownLatch latch = new CountDownLatch(num);
for (int i = 0; i < num; i++) {
int finalI = i;
CompletableFuture.runAsync(() -> {
try {
GenericObjectPool<MccClient> pool2 = MccClientPoolManager.getInstance()
.getPool("key", new GenericObjectPoolConfig<>());
MccClient mccClient = null;
try {
mccClient = pool2.borrowObject();
mccClient.putMc("" + finalI, "value" + finalI);
} finally {
if (Objects.nonNull(mccClient)) {
pool2.returnObject(mccClient);
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
} finally {
latch.countDown();
}
});
}
latch.await();
MccClientPoolManager.getInstance().shutdown("key");
}
源码解析-create()
510:private PooledObject<T> create() throws Exception
创建对象时锁住makeObjectCountLock,判断当前已创建对象是否大于设置的池大小,如果未超过则create==true
超过则判断已有的池大小是否为0,如果为0返回create==false,
不为0等待正在创建的对象,执行makeObjectCountLock.wait
create 为true则创建对象,执行 public PooledObject<T> makeObject()
创建完对象后判断是否为空,如果为空将已创建对象 cretaeCount--,并抛出异常
验证对象是否可用,如果不可用返回null,并cretaeCount--
在finally 中锁住 makeObjectCount lock,将makeObjectCount--,唤醒锁中所有等待的对象
将对象放入allObjects map中