文章目录
看本文前,强烈建议打开idea,跟着文章一起查看源码!
基本的类关系
我们从最底层的AutoCloseable接口开始
package java.lang;
/**
* 一个对象可能会持有一份资源(例如文件、socket句柄)直到该对象被关闭。
* 当实现了autoclose接口的对象在try-with-resources语法糖的参数()里被声明的话,这个对象
* 在try语句结束后,会自动调用close()方法,关闭对象,避免资源枯竭或者其他报错的出现。
* @author Josh Bloch
* @since 1.7
*/
public interface AutoCloseable {
void close() throws Exception;
}
* Note that unlike the {@link java.io.Closeable#close close}
* method of {@link java.io.Closeable}, this {@code close} method
* is <em>not</em> required to be idempotent.
它跟Closeable接口的差别是,这货不需要强制保持幂等,但是Closeable里的close的结果必须要保持幂等。
我们再看Closeable接口
package java.io;
import java.io.IOException;
public interface Closeable extends AutoCloseable {
public void close() throws IOException;
}
这个接口比较简单,只有一个要实现的方法,就是关闭的方法。我们看这个接口的注释,看看这个接口到底是用来做什么的。
@since 1.5,从JDK版本1.5开始。
所在io包下。
调用close方法,object所持有的目标资源或者数据,可以被关闭。
主要是用来关闭流的,以及关闭相关的系统资源的调用。如果资源早就被关闭了的话,则不会产生影响,也就是实现幂等。
需要注意的是,这个接口会报IO错误,强烈建议,在抛出异常之前,就抛弃这个资源,并将其标记为已经关闭。
翻译翻译,说人话就是,这个接口就是给资源类用的,资源一般在硬盘上有IO流程,当然是可以被关闭的,这个接口就是标注了这个行为,仅此而已,没啥复杂的。
我们来看下Pool类
public abstract class Pool<T> implements Closeable {
//它有一个protected属性
//包内可见,子类可见
protected GenericObjectPool<T> internalPool;
public void close() {
this.destroy();
}
public void destroy() {
this.closeInternalPool();
}
protected void closeInternalPool() {
try {
this.internalPool.close();
} catch (Exception var2) {
throw new JedisException("Could not destroy the pool", var2);
}
}
}
我们可以看到Pool是一个abstract class
什么是抽象类:
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。
重点就是,Pool类无法被直接实例化,只能是被继承。
再来看看Pool类对close接口的实现。
是调用了内部对象internalPool的close方法。
我们分析类,还是要从类的构造入手。
public abstract class Pool<T> implements Closeable {
public Pool() {
}
public Pool(GenericObjectPoolConfig poolConfig, PooledObjectFactory<T> factory) {
this.initPool(poolConfig, factory);
}
}
如上述代码,Pool类只含有2个构造函数,第一个什么都没有,咱们将目光聚焦于第二个
调用了一个initPool初始化池子的方法。
public void initPool(GenericObjectPoolConfig poolConfig, PooledObjectFactory<T> factory) {
if (this.internalPool != null) {
try {
this.closeInternalPool();
} catch (Exception var4) {
}
}
this.internalPool = new GenericObjectPool(factory, poolConfig);
}
这个代码的意思是,先去查看internalPool是否已经生成了一个线程池,如果已经存在的话,那就先关闭这个线程池this.closeInternalPool()
。
之后,再构造一个新的线程池。我们可以看出,所谓的构造一个新的线程池,就是new一个GenericObjectPool
对象给internalPool
我们看看是怎么new的这个GenericObjectPool
对象,GenericObjectPool
对象又是什么情况。
上图是GenericObjectPool
对象的继承、实现关系。
Pool类里调用的是这个构造函数。
我们来具体分析一下这个构造函数构造了什么样的类。
BaseGenericObjectPool<T>
类和GenericObjectPool<T>
类是package org.apache.commons.pool2.impl
包里的,所以,我们可以得出结论,jedis的线程池也是在apache包里的pool类的基础上构造出来的,实际上还是jdk里线程池工具类的使用。
解析GenericObjectPool的构造
//工厂类型,有哪些类别呢?
private volatile String factoryType;
//最大空闲数
private volatile int maxIdle;
//最小空闲数
private volatile int minIdle;
//被抛弃的配置?
private volatile AbandonedConfig abandonedConfig;
volatile 易变的,不稳定的,这个关键字是用来做并发编程的,使得多线程都可以看到该属性的最新值。精确地说就是,编译器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。
参考
private static final String ONAME_BASE = "org.apache.commons.pool2:type=GenericObjectPool,name=";
一个静态变量,看起来是用来拼接类名称的。
//工厂类
private final PooledObjectFactory<T> factory;
//一个键值对Map,键是IdentityWrapper类,值是被线程池管理的对象,可以在O(1)的时间复杂度下取到对应的线程对象
private final Map<IdentityWrapper<T>, PooledObject<T>> allObjects;
//原子类Long型的数字,为了并发
private final AtomicLong createCount;
//这个应该是跟下面那个Lock一起用的,应该也是用来计数的
private long makeObjectCount;
private final Object makeObjectCountLock;
//LinkedBlockingDeque并发编程里比较熟悉的类,双向阻塞队列,它里面的对象,跟allObjects管理的对象一致,Deque跟Queue还是有差别的
//是用一个全局独占锁,来把控并发安全的
private final LinkedBlockingDeque<PooledObject<T>> idleObjects;
GenericObjectPool
类的属性,我们已经大致了解了,我们看下它继承的父类BaseGenericObjectPool
里又有哪些属性呢?
public abstract class BaseGenericObjectPool<T> extends BaseObject{
//一个常量,似乎是用来存储
public static final int MEAN_TIMING_STATS_CACHE_SIZE = 100;
//一溜的属性设定,并且都是抗并发的
//最大总数
private volatile int maxTotal = -1;
//当资源耗尽是否锁住
private volatile boolean blockWhenExhausted = true;
//最大等待毫秒数
private volatile long maxWaitMillis = -1L;
//last in first out 后进先出
private volatile boolean lifo = true;
//是否是公平线程池
private final boolean fairness;
//是否在create进行测试,创造,可以理解
private volatile boolean testOnCreate = false;
//是否在Borrow进行测试,借?什么借,借什么
private volatile boolean testOnBorrow = false;
//是否在返回的时候进行测试
private volatile boolean testOnReturn = false;
//是否在空闲的时候进行测试
private volatile boolean testWhileIdle = false;
//作为验证连接是否有效的时间周期,如果testOnBorrow==false并且testWhileIdle==true,则在应用获取连接的时候会判断连接的空闲时间是否大于timeBetweenEvictionRunsMillis,如果大于则会验证该连接是否有效。
private volatile long timeBetweenEvictionRunsMillis = -1L;
//测试的次数
private volatile int numTestsPerEvictionRun = 3;
private volatile long minEvictableIdleTimeMillis = 1800000L;
private volatile long softMinEvictableIdleTimeMillis = -1L;
private volatile EvictionPolicy<T> evictionPolicy;
private volatile long evictorShutdownTimeoutMillis = 10000L;
final Object closeLock = new Object();
//是否关闭
volatile boolean closed = false;
final Object evictionLock = new Object();
private BaseGenericObjectPool<T>.Evictor evictor = null;
BaseGenericObjectPool<T>.EvictionIterator evictionIterator = null;
private final WeakReference<ClassLoader> factoryClassLoader;
private final ObjectName oname;
private final String creationStackTrace;
private final AtomicLong borrowedCount = new AtomicLong(0L);
private final AtomicLong returnedCount = new AtomicLong(0L);
final AtomicLong createdCount = new AtomicLong(0L);
final AtomicLong destroyedCount = new AtomicLong(0L);
final AtomicLong destroyedByEvictorCount = new AtomicLong(0L);
final AtomicLong destroyedByBorrowValidationCount = new AtomicLong(0L);
private final BaseGenericObjectPool<T>.StatsStore activeTimes = new BaseGenericObjectPool.StatsStore(100);
private final BaseGenericObjectPool<T>.StatsStore idleTimes = new BaseGenericObjectPool.StatsStore(100);
private final BaseGenericObjectPool<T>.StatsStore waitTimes = new BaseGenericObjectPool.StatsStore(100);
private final AtomicLong maxBorrowWaitTimeMillis = new AtomicLong(0L);
private volatile SwallowedExceptionListener swallowedExceptionListener = null;
}
我有时候会觉得java的类名称太长了,现在觉得也挺好的,名字长其中包含的意义更清晰明了。
其中timeBetweenEvictionRunsMillis
的使用,可以参考这篇文章,主要是用来判断闲暇时间是否到达标准。
总体看下来,这个父类属性居多,并不涉及具体的线程对象的管理,理解了这一点,我们可以放心了,其实就是一个属性库,可以这么理解。
回到GenericObjectPool
的解析。
GenericObjectPool的构造类
public class GenericObjectPool<T> extends BaseGenericObjectPool<T> implements ObjectPool<T>, GenericObjectPoolMXBean, UsageTracking<T> {
public GenericObjectPool(PooledObjectFactory<T> factory) {
this(factory, new GenericObjectPoolConfig());
}
public GenericObjectPool(PooledObjectFactory<T> factory, GenericObjectPoolConfig config) {
//代码xxx
}
public GenericObjectPool(PooledObjectFactory<T> factory, GenericObjectPoolConfig config, AbandonedConfig abandonedConfig) {
this(factory, config);
this.setAbandonedConfig(abandonedConfig);
}
}
虽然有3个构造函数,其实第一个和第三个,全都是调用第二个构造函数。
所以,我们来详细解析下第二个构造函数
public GenericObjectPool(PooledObjectFactory<T> factory, GenericObjectPoolConfig config) {
//将配置传递进父类属性池
super(config, "org.apache.commons.pool2:type=GenericObjectPool,name=", config.getJmxNamePrefix());
//其实我蛮好奇factoryType是什么的,不过没有
this.factoryType = null;
//最大空闲数8个
this.maxIdle = 8;
//最小空闲数0个,就是线程全满,也不会新增一个出来
this.minIdle = 0;
//管理所有线程对象的是一个concurrentHashMap,使得并发保持安全
this.allObjects = new ConcurrentHashMap();
//初始化为0,long类型
this.createCount = new AtomicLong(0L);
//初始化为0,long类型
this.makeObjectCount = 0L;
this.makeObjectCountLock = new Object();
this.abandonedConfig = null;
if (factory == null) {
//如果没有这个factory工厂类的话,会直接报错
this.jmxUnregister();
throw new IllegalArgumentException("factory may not be null");
} else {
this.factory = factory;
//为空闲阻塞队列赋值
this.idleObjects = new LinkedBlockingDeque(config.getFairness());
//这里可以传入自定义的属性,包括max_idle,min_idle,max_total什么什么的
this.setConfig(config);
this.startEvictor(this.getTimeBetweenEvictionRunsMillis());
}
}
线程池的构造已经大致明了。
但是线程池最重要的功能我们还未涉及,那就是存取PooledObject,也就是线程对象。
PooledObject
类是一个接口类,我们可以看它的默认实现类。
public class DefaultPooledObject<T> implements PooledObject<T> {
//实装对象
private final T object;
//线程池对象状态,这个蛮有意思的
private PooledObjectState state;
//创造的时间
private final long createTime;
//上一次被Borrow的时间
private volatile long lastBorrowTime;
//上一次使用的时间
private volatile long lastUseTime;
//上一次返回的时间
private volatile long lastReturnTime;
//被抛弃是否要log
private volatile boolean logAbandoned;
//上一次borrowby哪个调用栈
private final CallStack borrowedBy;
//上一次usedBy哪个调用栈
private final CallStack usedBy;
//borrowed的次数
private volatile long borrowedCount;
}
线程对象的状态,这几个状态值得注意。
public enum PooledObjectState {
//空闲
IDLE,
ALLOCATED,
EVICTION,
EVICTION_RETURN_TO_HEAD,
VALIDATION,
VALIDATION_PREALLOCATED,
VALIDATION_RETURN_TO_HEAD,
INVALID,
ABANDONED,
RETURNING;
private PooledObjectState() {
}
}
GenericObjectPool的内部方法
public class GenericObjectPool<T> extends BaseGenericObjectPool<T> implements ObjectPool<T>, GenericObjectPoolMXBean, UsageTracking<T> {
//借用对象,需要注意的是,这里的对象指的的是实现了PooledObject的对象
public T borrowObject() throws Exception {
return this.borrowObject(this.getMaxWaitMillis());
}
//借用对象(传最大等待时间)
public T borrowObject(long borrowMaxWaitMillis) throws Exception {}
//返回对象
public void returnObject(T obj){}
//对象失效
public void invalidateObject(T obj) throws Exception{}
//清空
public void clear(){}
//获得活跃对象的数量
public int getNumActive(){}
//获得休闲中对象的数量
public int getNumIdle(){}
//关闭线程池
public void close(){}
//evict
public void evict() throws Exception{}
//准备线程池
public void preparePool() throws Exception{}
//创造对象
private PooledObject<T> create() throws Exception{}
//毁灭对象
private void destroy(PooledObject<T> toDestroy) throws Exception{}
//保证最小空闲对象
void ensureMinIdle() throws Exception{}
//确保空闲对象数量
private void ensureIdle(int idleCount, boolean always) throws Exception{}
//新增对象
public void addObject() throws Exception {}
//新增空闲对象
private void addIdleObject(PooledObject<T> p){}
//获取正在test的对象数量
private int getNumTests() {}
//卸载AbandonedConfig ac属性
private void removeAbandoned(AbandonedConfig ac){}
//使用对象
public void use(T pooledObject){}
//获取wait状态的对象数量
public int getNumWaiters() {}
//获取工厂类名称
public String getFactoryType() {}
//获取所有对象
public Set<DefaultPooledObjectInfo> listAllObjects(){}
//转成string字符
protected void toStringAppendFields(StringBuilder builder){}
}
里面还有一些get,set进行属性配置的method没有提及,各位读者可以自行去查看,对整体逻辑来说没有太大的关系。
下一章,我们将具体解析,线程池的关键动作。
preparePool(),create(),addObject(),borrowObject(),evict(),use()
clear(),close()
敬请期待☺