Jedis线程池的实现原理(一)


看本文前,强烈建议打开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()

敬请期待☺

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

rgbhi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值