导言
连接是一项程序运行中比较重的资源获取,受限于网络的不可信等因素,创建连接的开销一般是比较巨大的,所以才会有各种池化技术的出现,同理于线程池技术,都是为了减少创建连接的耗时,所以线程池技术基本是在高并发的业务领域必须掌握的一项技术
连接池的技术
common-pool2技术是比较常见的一种连接池,在redis的java客户端jedis中是默认的连接池实现,在这里不鼓励大家去自己实现一个连接池,因为现成的轮子即使有问题,大部分开源的技术也是支持修改自定义的,在大量使用中经过了大流量考验的一种技术;所以在这里着重介绍这种常用的连接池技术
get start
首先引入maven依赖:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.4.3</version>
</dependency>
common-pool2的核心类是GenericObjectPool类,这个类的作用主要是用来自定义一些配置和池中对象创建的工厂方法,可以看一下具体的代码实现:
public GenericObjectPool(PooledObjectFactory<T> factory,
GenericObjectPoolConfig config) {
super(config, ONAME_BASE, config.getJmxNamePrefix());
if (factory == null) {
jmxUnregister(); // tidy up
throw new IllegalArgumentException("factory may not be null");
}
this.factory = factory;
idleObjects = new LinkedBlockingDeque<PooledObject<T>>(config.getFairness());
setConfig(config);
startEvictor(getTimeBetweenEvictionRunsMillis());
}
该类的构造方法有两个参数,PooledObjectFactory是创建池中对象的工厂对象,GenericObjectPoolConfig是连接池的配置对象。
连接池配置
最能直接提升工程效率的学习就是能立竿见影的知识,在连接连接池的源码之前可以先了解一下连接池的主要配置,看看良好的抽象是怎么做的:
maxTotal:池中对象的最大可创建数量,默认为8;
maxIdle:池中对象的最大空闲数量,默认也是8;
minIdle:池中对象的最小空闲数量,默认是0;
timeBetweenEvictionRunsMillis:空闲对象检测线程的执行周期,单位是ms,默认-1,小于等于0则不执行检测;
numTestsPerEvictionRun:检测空闲对象的数量,如果小于等于0,那么每次检测的是当前空闲数量/该配置的绝对值,,对结果向上取整;
testOnCreate:创建对象是检测对象是否有效;默认false;
testOnBorrow:在从对象池中获取对象时检测对象有效性,同上;
testOnReturn:在归还对象时检测有效,同上;
testWhileIdle:在检测空闲对象不需要移除时,是否对有效性进行检测,同上;
blockWhenExhausted:当对象池没有空闲对象时,新的获取对象的请求是否阻塞,默认true;
maxWaitMillis:表示等待空闲连接的最大时间,如果小于等于0,那么永不超时,一直阻塞等待,只有当大于0时才会生效,超过这个时间后会抛出NoSuchElementException的异常。默认-1,单位milis。
minEvictableIdleTimeMillis:对象空闲后强制移除的时间,小于等于0会被赋值为Long.MAX,大于0时才会生效,表示这个元素空闲时间的最大值。只要超过这个时间,那么就会无视minIdle的配置移除这个对象;
softMinEvictableIdleTimeMillis:当数量大于minIdle时对象被移除的最大空闲时间,赋值规则同上;
主要方法详细解释
1.最重要的方法当然是创建资源的方法了:
private PooledObject<T> create() throws Exception {
//首先获取连接最大值
int localMaxTotal = getMaxTotal();
// This simplifies the code later in this method
//如果小于0,那么复制为这
if (localMaxTotal < 0) {
localMaxTotal = Integer.MAX_VALUE;
}
// Flag that indicates if create should:
// - TRUE: call the factory to create an object
// - FALSE: return null
// - null: loop and re-test the condition that determines whether to
// call the factory
//用来标记是否需要创建的标志
Boolean create = null;
while (create == null) {
//加锁进行创建,线程安全
synchronized (makeObjectCountLock) {
//创建计数
final long newCreateCount = createCount.incrementAndGet();
//和最大数量进行对比
if (newCreateCount > localMaxTotal) {
// The pool is currently at capacity or in the process of
// making enough new objects to take it to capacity.
//如果已经超过最大计数,那么就将计数减1
createCount.decrementAndGet();
if (makeObjectCount == 0) {
// There are no makeObject() calls in progress so the
// pool is at capacity. Do not attempt to create a new
// object. Return and wait for an object to be returned
//没有其他正在调用的创建资源的方法,直接返回失败,等待资源的返回
create = Boolean.FALSE;
} else {
// There are makeObject() calls in progress that might
// bring the pool to capacity. Those calls might also
// fail so wait until they complete and then re-test if
// the pool is at capacity or not.
//如果有其他方法正在创建对象,那么就阻塞当前线程,并释放锁
makeObjectCountLock.wait();
}
} else {
// The pool is not at capacity. Create a new object.
//创建标志置为真,方法调用数+ 1
makeObjectCount++;
create = Boolean.TRUE;
}
}
}
//判断是否需要创建,不需要直接返回
if (!create.booleanValue()) {
return null;
}
final PooledObject<T> p;
try {
//调用工厂方法来创建对象
p = factory.makeObject();
} catch (final Exception e) {
createCount.decrementAndGet();
throw e;
} finally {
synchronized (makeObjectCountLock) {
//方法调用标记-1
makeObjectCount--;
//唤醒其他阻塞在这个对象上的线程
makeObjectCountLock.notifyAll();
}
}
final AbandonedConfig ac = this.abandonedConfig;
if (ac != null && ac.getLogAbandoned()) {
p.setLogAbandoned(true);
}
//已创建计数
createdCount.incrementAndGet();
allObjects.put(new IdentityWrapper<T>(p.getObject()), p);
return p;
}