jedis对象池原理和源码分析

1. 什么是对象池?

      我们都知道一个对象,比如car其生命周期大致可分为“创建”,“使用”, “销毁”三个阶段,如果每次创建、使用完一个对象就释放掉,等到需要新的的对象再重新创建,当创建一个对象的成本比较大时(比如数据库连接等),这样会非常消耗资源。为了节约系统资源,就需要就这些之前创建过的对象保存下来,等待下次需要时直接使用,这种用于充当保存对象的“容器”就是对象池,它使得对象可复用,减少频繁创建造成的开销。

2. 对象池原理

     试想如果让你来实现一个对象池,应该怎么做呢? 首先,对象池肯定是用来管理对象的,举个例子,我们可以把对象看成是一台公共自行车,将对象池比作一个自行车站点,首先,对于这个站点的功能,肯定有‘借’自行车,“还”自行车,定期“检查”自行车车况,“销毁”(坏了的,拿去修或者销毁)这几个基本的功能。但站点一般只负责管理车,不会自己造自行车,还需要一个自行车工厂去生产车,而这个造自行车的工厂除了生产车,还需要在对车进行“检测”、“出库”功能、“回收”等功能。
      jedis使用的pool是apache实现的对象池,它是否和我们想象的一样?大同小异!!在其pool组件中,对象池化工作被划分了三类对象: PooledObjectFactory, ObjectPool, ObjectPoolFactory。

2.1 PooledObjectFactory

     PooledObjectFactory用于管理被池化的对象的产生、激活、挂起、校验和销毁,它是一个接口,使用者根据是什么工厂来具体实现。

public interface PooledObjectFactory<T> {

  // 用于产生新的对象
  PooledObject<T> makeObject() throws Exception;
  // 用于销毁被validateObject判定为已失效的对象
  void destroyObject(PooledObject<T> p) throws Exception;

 // 用于校验一个具体的对象是否仍然有效,如果对象失效会被destroyObject 方法销毁
  boolean validateObject(PooledObject<T> p);

 //  将对象重新设置为初始状态
  void activateObject(PooledObject<T> p) throws Exception;
// 将这个对象挂起,设置为休眠状态
  void passivateObject(PooledObject<T> p) throws Exception;
}

      jedis自定义的Factory为JedisFactory:

class JedisFactory implements PooledObjectFactory<Jedis> {
  private final AtomicReference<HostAndPort> hostAndPort = new AtomicReference<HostAndPort>();
  private final int connectionTimeout;
  private final int soTimeout;
  private final String password;
  private final int database;
  private final String clientName;

  public JedisFactory(final String host, final int port, final int connectionTimeout,
      final int soTimeout, final String password, final int database, final String clientName) {
    this.hostAndPort.set(new HostAndPort(host, port));
    this.connectionTimeout = connectionTimeout;
    this.soTimeout = soTimeout;
    this.password = password;
    this.database = database;
    this.clientName = clientName;
  }

  public JedisFactory(final URI uri, final int connectionTimeout, final int soTimeout,
      final String clientName) {
    if (!JedisURIHelper.isValid(uri)) {
      throw new InvalidURIException(String.format(
        "Cannot open Redis connection due invalid URI. %s", uri.toString()));
    }

    this.hostAndPort.set(new HostAndPort(uri.getHost(), uri.getPort()));
    this.connectionTimeout = connectionTimeout;
    this.soTimeout = soTimeout;
    this.password = JedisURIHelper.getPassword(uri);
    this.database = JedisURIHelper.getDBIndex(uri);
    this.clientName = clientName;
  }

  public void setHostAndPort(final HostAndPort hostAndPort) {
    this.hostAndPort.set(hostAndPort);
  }

  @Override
  public void activateObject(PooledObject<Jedis> pooledJedis) throws Exception {
    final BinaryJedis jedis = pooledJedis.getObject();
    if (jedis.getDB() != database) {
      jedis.select(database);
    }
  }

  @Override
  public void destroyObject(PooledObject<Jedis> pooledJedis) throws Exception {
    final BinaryJedis jedis = pooledJedis.getObject();
    if (jedis.isConnected()) {
      try {
        try {
          jedis.quit();
        } catch (Exception e) {
        }
        jedis.disconnect();
      } catch (Exception e) {

      }
    }
  }

  @Override
  public PooledObject<Jedis> makeObject() throws Exception {
    final HostAndPort hostAndPort = this.hostAndPort.get();
    final Jedis jedis = new Jedis(hostAndPort.getHost(), hostAndPort.getPort(), connectionTimeout,
        soTimeout);

    try {
      jedis.connect();
      if (null != this.password) {
        jedis.auth(this.password);
      }
      if (database != 0) {
        jedis.select(database);
      }
      if (clientName != null) {
        jedis.clientSetname(clientName);
      }
    } catch (JedisException je) {
      jedis.close();
      throw je;
    }
    return new DefaultPooledObject<Jedis>(jedis);

  }

  @Override
  public void passivateObject(PooledObject<Jedis> pooledJedis) throws Exception {
    // TODO maybe should select db 0? Not sure right now.
  }

  @Override
  public boolean validateObject(PooledObject<Jedis> pooledJedis) {
    final BinaryJedis jedis = pooledJedis.getObject();
    try {
      HostAndPort hostAndPort = this.hostAndPort.get();

      String connectionHost = jedis.getClient().getHost();
      int connectionPort = jedis.getClient().getPort();

      return hostAndPort.getHost().equals(connectionHost)
          && hostAndPort.getPort() == connectionPort && jedis.isConnected()
          && jedis.ping().equals("PONG");
    } catch (final Exception e) {
      return false;
    }
  }
}
2.2 ObjectPool

      ObjectPool用于管理要被池化的对象的借出和归还,它也是在org.apache.commons.pool包中定义的一个接口,它有多种实现,GenericObjectPool是其中的一种,而jedis使用的对象池就是这个pool。
一个对象池往往需要配置很多合适的参数才能使用,对GenericObjectPool的配置是通过org.apache.commons.pool.impl.GenericObjectPool.Config来完成,这是个简单的数值对象,每个成员都预设了默认值,这里我们详细介绍一下里面的各个成员的含义。

2.1 GenericObjectPool 配置详情
  • maxActive 控制池中对象的最大数量。默认值是8,如果是负值表示没限制。
  • maxIdle 控制池中空闲的对象的最大数量。默认值是8,如果是负值表示没限制。
  • minIdle
    控制池中空闲的对象的最小数量。默认值是0。
  • whenExhaustedAction 指定池中对象被消耗完以后的行为,有下面这些选择:
WHEN_EXHAUSTED_FAIL               0

WHEN_EXHAUSTED_BLOCK              1

    如果是WHEN_EXHAUSTED_FAIL,当池中对象达到上限以后,继续borrowObject会抛出NoSuchElementException异常。
    如果是WHEN_EXHAUSTED_BLOCK,当池中对象达到上限以后,会一直等待,直到有一个对象可用。这个行为还与maxWait有关,如果maxWait是正数,那么会等待maxWait的毫秒的时间,超时会抛出NoSuchElementException异常;如果maxWait为负值,会永久等待。
    whenExhaustedAction 的默认值是WHEN_EXHAUSTED_BLOCK,maxWait的默认值是-1。

  • maxWaitMillis 如果whenExhaustedAction 是WHEN_EXHAUSTED_BLOCK,指定等待的毫秒数。如果maxWait是正数,那么会等待maxWait的毫秒的时间,超时会抛出NoSuchElementException异常;如果maxWait为负值,会永久等待。
  • testOnBorrow 如果testOnBorrow被设置,pool会在borrowObject返回对象之前使用PoolableObjectFactory的validateObject来验证这个对象是否有效,要是对象没通过验证,这个对象会被丢弃,然后重新创造一个新的对象。testOnBorrow的默认值是false。
  • testOnReturn 如果testOnReturn被设置,pool会在returnObject的时候通过PoolableObjectFactory的validateObject方法验证对象,如果对象没通过验证,对象会被丢弃,不会被放到池中。testOnReturn的默认值是false。
  • testWhileIdle 在检测空闲对象线程检测到对象不需要移除时,是否检测对象的有效性。true是,默认值是false。
    这个设置仅在timeBetweenEvictionRunsMillis被设置成正值(>0)的时候才会生效。 testWhileIdle的默认值是false。
  • timeBetweenEvictionRunsMillis 空闲对象检测线程的执行周期,即多长时候执行一次空闲对象检测。单位是毫秒数。如果小于等于0,则不执行检测线程。默认值是-1;
2.2 代码讲解

只看参数根本看不出它们都是做什么的,还是得从代码入手,先看其uml图:


image.png

可以看到GenericObjectPool继承了BaseGenericObjectPool,实现了ObjectPool等接口,使用泛型T来指定缓存对象的类型。BaseGenericObjectPool主要做了一些公共的实现,GenericObjectPool则是复写了一些抽象方法,做具体的实现。

public GenericObjectPool(final PooledObjectFactory<T> factory) {
        this(factory, new GenericObjectPoolConfig<T>());
    }

 public GenericObjectPool(final PooledObjectFactory<T> factory,
            final GenericObjectPoolConfig<T> 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<>(config.getFairness());
// 设置参数
        setConfig(config);
    }
public LinkedBlockingDeque(final boolean fairness) {
        this(Integer.MAX_VALUE, fairness);
    }

public LinkedBlockingDeque(final int capacity, final boolean fairness) {
        if (capacity <= 0) {
            throw new IllegalArgumentException();
        }
        this.capacity = capacity;
        lock = new InterruptibleReentrantLock(fairness);
        notEmpty = lock.newCondition();
        notFull = lock.newCondition();
    }

可以看到,构造GenericObjectPool的关键参数是PooledObjectFactory,是对象池的使用者自己需要实现的用于产生池化对象的工厂,对于jedis而言,该工厂就是前面介绍的JedisFactory。可以看出对象池和工厂是解耦的,对象池只负责管理对象,至于生产、激活、销毁等由具体的工厂提供。
GenericObjectPool创建了一个双端队列LinkedBlockingDeque作为idle队列缓存所有空闲的对象。而BlockingDeque的锁竞争,默认使用非公平锁。

public void setConfig(GenericObjectPoolConfig conf) {
        setLifo(conf.getLifo());
        setMaxIdle(conf.getMaxIdle());
        setMinIdle(conf.getMinIdle());
        setMaxTotal(conf.getMaxTotal());
        setMaxWaitMillis(conf.getMaxWaitMillis());
        setBlockWhenExhausted(conf.getBlockWhenExhausted());
        setTestOnCreate(conf.getTestOnCreate());
        setTestOnBorrow(conf.getTestOnBorrow());
        setTestOnReturn(conf.getTestOnReturn());
        setTestWhileIdle(conf.getTestWhileIdle());
        setNumTestsPerEvictionRun(conf.getNumTestsPerEvictionRun());
        setMinEvictableIdleTimeMillis(conf.getMinEvictableIdleTimeMillis());
        setTimeBetweenEvictionRunsMillis(
                conf.getTimeBetweenEvictionRunsMillis());
        setSoftMinEvictableIdleTimeMillis(
                conf.getSoftMinEvictableIdleTimeMillis());
        setEvictionPolicyClassName(conf.getEvictionPolicyClassName());
    }
public final void setTimeBetweenEvictionRunsMillis(
            final long timeBetweenEvictionRunsMillis) {
        this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
        startEvictor(timeBetweenEvictionRunsMillis);
    }

在参数设置setTimeBetweenEvictionRunsMillis方法里会开启一个TimerTask对idle队列进行定时扫描,必要时进行淘汰

 final void startEvictor(final long delay) {
        synchronized (evictionLock) {
            if (null != evictor) {
                EvictionTimer.cancel(evictor, evictorShutdownTimeoutMillis, TimeUnit.MILLISECONDS);
                evictor = null;
                evictionIterator = null;
            }
            if (delay > 0) {
                evictor = new Evictor();
                EvictionTimer.schedule(evictor, delay, delay);
            }
        }
    }

startEvictor主要用于创建新的Evictor,然后基于ScheduledThreadPoolExecutor进行线程调度。

class Evictor extends TimerTask {
        @Override
        public void run() {
            ClassLoader savedClassLoader =
                    Thread.currentThread().getContextClassLoader();
            try {
                if (factoryClassLoader != null) {
                    // Set the class loader for the factory
                    ClassLoader cl = factoryClassLoader.get();
                    if (cl == null) {
                        // The pool has been dereferenced and the class loader
                        // GC'd. Cancel this timer so the pool can be GC'd as
                        // well.
                        cancel();
                        return;
                    }
                    Thread.currentThread().setContextClassLoader(cl);
                }

                // Evict from the pool
                try {
                    evict();
                } catch(Exception e) {
                    swallowException(e);
                } catch(OutOfMemoryError oome) {
                    // Log problem but give evictor thread a chance to continue
                    // in case error is recoverable
                    oome.printStackTrace(System.err);
                }
                // Re-create idle instances.
                try {
                    ensureMinIdle();
                } catch (Exception e) {
                    swallowException(e);
                }
            } finally {
                // Restore the previous CCL
                Thread.currentThread().setContextClassLoader(savedClassLoader);
            }
        }
    }

Evictor的run方法主要调用了evict()方法

// 对空闲的连接进行淘汰清理
public void evict() throws Exception {
        assertOpen();

        if (idleObjects.size() > 0) {

            PooledObject<T> underTest = null;
            // 获取淘汰策略
            final EvictionPolicy<T> evictionPolicy = getEvictionPolicy();

            synchronized (evictionLock) {
                final EvictionConfig evictionConfig = new EvictionConfig(
                        getMinEvictableIdleTimeMillis(),
                        getSoftMinEvictableIdleTimeMillis(),
                        getMinIdle());

                final boolean testWhileIdle = getTestWhileIdle();
                // 为一次淘汰策略运行扫描多少个对象
                for (int i = 0, m = getNumTests(); i < m; i++) {
                    if (evictionIterator == null || !evictionIterator.hasNext()) {
                        evictionIterator = new EvictionIterator(idleObjects);
                    }
                    if (!evictionIterator.hasNext()) {
                        // Pool exhausted, nothing to do here
                        return;
                    }

                    try {
                        underTest = evictionIterator.next();
                    } catch (final NoSuchElementException nsee) {
                        // Object was borrowed in another thread
                        // Don't count this as an eviction test so reduce i;
                        i--;
                        evictionIterator = null;
                        continue;
                    }
                    // 将当前空闲的连接设置为淘汰状态,如果该连接不是空闲状态则重新迭代一个
                    if (!underTest.startEvictionTest()) {
                        // Object was borrowed in another thread
                        // Don't count this as an eviction test so reduce i;
                        i--;
                        continue;
                    }

                    // User provided eviction policy could throw all sorts of
                    // crazy exceptions. Protect against such an exception
                    // killing the eviction thread.
                    boolean evict;
                    try {
                         // 根据淘汰策略判断是否需要淘汰
                        evict = evictionPolicy.evict(evictionConfig, underTest,
                                idleObjects.size());
                    } catch (final Throwable t) {
                        // Slightly convoluted as SwallowedExceptionListener
                        // uses Exception rather than Throwable
                        PoolUtils.checkRethrow(t);
                        swallowException(new Exception(t));
                        // Don't evict on error conditions
                        evict = false;
                    }

                    if (evict) {
                        // 如果被判定为需要淘汰,则销毁对象
                        destroy(underTest);
                        destroyedByEvictorCount.incrementAndGet();
                    } else { // 不需要被淘汰
                        if (testWhileIdle) {
                             // 对对象进行有效期校验
                            boolean active = false;
                            try {
                                factory.activateObject(underTest);
                                active = true;
                            } catch (final Exception e) {
                                destroy(underTest);
                                destroyedByEvictorCount.incrementAndGet();
                            }
                            if (active) {
                                 // 对对象的有效性进行检测
                                if (!factory.validateObject(underTest)) {
                                    destroy(underTest);
                                    destroyedByEvictorCount.incrementAndGet();
                                } else {
                                    try {
                                        factory.passivateObject(underTest);
                                    } catch (final Exception e) {
                                        destroy(underTest);
                                        destroyedByEvictorCount.incrementAndGet();
                                    }
                                }
                            }
                        }
                        if (!underTest.endEvictionTest(idleObjects)) {
                            // TODO - May need to add code here once additional
                            // states are used
                        }
                    }
                }
            }
        }
        final AbandonedConfig ac = this.abandonedConfig;
        if (ac != null && ac.getRemoveAbandonedOnMaintenance()) {
            removeAbandoned(ac);
        }
    }
  • numTestsPerEvictionRun 设置驱逐线程每次检测对象的数量。
    这个设置仅在timeBetweenEvictionRunsMillis被设置成正值(>0)的时候才会生效。numTestsPerEvictionRun的默认值是3。
  • minEvictableIdleTimeMillis 指定最小的空闲驱逐的时间间隔(空闲超过指定的时间的对象,会被清除掉)。这个设置仅在timeBetweenEvictionRunsMillis被设置成正值(>0)的时候才会生效。minEvictableIdleTimeMillis默认值是30分钟。
  • softMinEvictableIdleTimeMillis 与minEvictableIdleTimeMillis类似,也是指定最小的空闲驱逐的时间间隔(空闲超过指定的时间的对象,会被清除掉),不过会参考minIdle的值,只有idle对象的数量超过minIdle的值,对象才会被清除。这个设置仅在timeBetweenEvictionRunsMillis被设置成正值(>0)的时候才会生效,并且这个配置能被minEvictableIdleTimeMillis配置取代(minEvictableIdleTimeMillis配置项的优先级更高)。
    softMinEvictableIdleTimeMillis的默认值是-1。
  • lifo pool可以被配置成LIFO队列(last-in-first-out)或FIFO队列(first-in-first-out),来指定空闲对象被使用的次序。lifo的默认值是true。
    驱逐线程采取的淘汰策略是默认策略:
public class DefaultEvictionPolicy<T> implements EvictionPolicy<T> {

    @Override
    public boolean evict(final EvictionConfig config, final PooledObject<T> underTest,
            final int idleCount) {
        // 当对象的空闲时间大于SoftMinEvictableIdleTimeMillis并且实际空闲对象数目大于配置的空闲对象时会被淘汰
        // 或者时间大约IdleEvictTime 也会被淘汰
        if ((config.getIdleSoftEvictTime() < underTest.getIdleTimeMillis() &&
                config.getMinIdle() < idleCount) ||
                config.getIdleEvictTime() < underTest.getIdleTimeMillis()) {
            return true;
        }
        return false;
    }
}

空闲淘汰线程默认是不开启的,是可有可无,接下来我们重点来看下对象的借、取和归还。
     要想从对象池获取一个对象,GenericObjectPool提供的接口是borrowObject:

  @Override
    public T borrowObject() throws Exception {
        // maxWaitMillis 当获取不到对象需要等待的时间
        return borrowObject(getMaxWaitMillis());
    }
 public T borrowObject(final long borrowMaxWaitMillis) throws Exception {
        assertOpen();

        final AbandonedConfig ac = this.abandonedConfig;
        if (ac != null && ac.getRemoveAbandonedOnBorrow() &&
                (getNumIdle() < 2) &&
                (getNumActive() > getMaxTotal() - 3) ) {
            removeAbandoned(ac);
        }

        PooledObject<T> p = null;

        // Get local copy of current config so it is consistent for entire
        // method execution
        // 当资源耗尽时是否阻塞
        final boolean blockWhenExhausted = getBlockWhenExhausted();

        boolean create;
        final long waitTime = System.currentTimeMillis();

        while (p == null) {
            create = false;
            // 弹出一个对象
            p = idleObjects.pollFirst();
            if (p == null) {
                // 池里已经没有空闲的对象,需要创建一个,可能创建成功也可能失败
                p = create();
                if (p != null) {
                    create = true;
                }
            }
            if (blockWhenExhausted) {
                if (p == null) {
                    // 走到这 说明前面创建失败了 ,如果没有设置超时时间会调用take阻塞,知道有资源
                    if (borrowMaxWaitMillis < 0) {
                        p = idleObjects.takeFirst();
                    } else {
                        // 阻塞调用 等到borrowMaxWaitMillis返回
                        p = idleObjects.pollFirst(borrowMaxWaitMillis,
                                TimeUnit.MILLISECONDS);
                    }
                }
                if (p == null) {
                    // 等待后仍然没有获取 抛出异常
                    throw new NoSuchElementException(
                            "Timeout waiting for idle object");
                }
            } else { // 如果耗尽不需要阻塞 直接抛出异常
                if (p == null) {
                    throw new NoSuchElementException("Pool exhausted");
                }
            }
            if (!p.allocate()) { // 判断当前的状态是否是idle,若是则设置为allocated
                p = null;
            }

            if (p != null) { // 获取到对象后,进行一系列的校验操作
                try {
                    // 激活对象
                    factory.activateObject(p);
                } catch (final Exception e) {
                    try {
                        destroy(p);
                    } catch (final Exception e1) {
                        // Ignore - activation failure is more important
                    }
                    p = null;
                    if (create) {
                        final NoSuchElementException nsee = new NoSuchElementException(
                                "Unable to activate object");
                        nsee.initCause(e);
                        throw nsee;
                    }
                }
                // 对象要么从池中取出来,要么是新创建出来,根据配置的参数testOnBorrow或者testOnCreate对对象进行test
                if (p != null && (getTestOnBorrow() || create && getTestOnCreate())) {
                    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);
                            destroyedByBorrowValidationCount.incrementAndGet();
                        } catch (final Exception e) {
                            // Ignore - validation failure is more important
                        }
                        p = null;
                        if (create) {
                            final NoSuchElementException nsee = new NoSuchElementException(
                                    "Unable to validate object");
                            nsee.initCause(validationThrowable);
                            throw nsee;
                        }
                    }
                }
            }
        }
        // 更新一些统计信息
        updateStatsBorrow(p, System.currentTimeMillis() - waitTime);

        return p.getObject();
    }

从borrowObject方法可以看出,对象是采取懒加载的方式进行创建,一开始对象池并没有对象,在从对象池取对象时方才create

 private PooledObject<T> create() throws Exception {
        int localMaxTotal = getMaxTotal();
        // This simplifies the code later in this method
        if (localMaxTotal < 0) {
            localMaxTotal = Integer.MAX_VALUE;
        }

        final long localStartTimeMillis = System.currentTimeMillis();
        final long localMaxWaitTimeMillis = Math.max(getMaxWaitMillis(), 0);

        // 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.
                    // 超过了容量,回滚下
                    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.
                        // 如果正在创建的对象数目不是0,这些创建可能失败,所以可以等待一下,重新测试
                        makeObjectCountLock.wait(localMaxWaitTimeMillis);
                    }
                } else {
                    // The pool is not at capacity. Create a new object.
                    // 没有达到容量就直接创建
                    makeObjectCount++;
                    create = Boolean.TRUE;
                }
            }

            // Do not block more if maxWaitTimeMillis is set.
            // 那么设置maxWaitTimeMillis 有什么意义呢?
            if (create == null &&
                (localMaxWaitTimeMillis > 0 &&
                 System.currentTimeMillis() - localStartTimeMillis >= localMaxWaitTimeMillis)) {
                create = Boolean.FALSE;
            }
        }

        if (!create.booleanValue()) {
            return null;
        }

        final PooledObject<T> p;
        try {
            // 工厂创建对象
            p = factory.makeObject();
        } catch (final Throwable 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);
            // TODO: in 3.0, this can use the method defined on PooledObject
            if (p instanceof DefaultPooledObject<?>) {
                ((DefaultPooledObject<T>) p).setRequireFullStackTrace(ac.getRequireFullStackTrace());
            }
        }
        
        // 增加创建的数量
        createdCount.incrementAndGet();
        // 对象创建成功后放到allObjects Map中 注意不是idle Map
        allObjects.put(new IdentityWrapper<>(p.getObject()), p);
        return p;
    }

可以看到,新创建的对象一开始并不是放在idle队列中,只有在对象return的时候才返回到idle队列,新创建的对象再borrowObject时被设置为allocated状态

  public synchronized boolean allocate() {
        if (state == PooledObjectState.IDLE) {
            // 将对象设置为Allocated状态
            state = PooledObjectState.ALLOCATED;
            // 设置借出时间
            lastBorrowTime = System.currentTimeMillis();
            lastUseTime = lastBorrowTime;
            borrowedCount++;
            if (logAbandoned) {
                borrowedBy.fillInStackTrace();
            }
            return true;
        } else if (state == PooledObjectState.EVICTION) {
            // TODO Allocate anyway and ignore eviction test
            state = PooledObjectState.EVICTION_RETURN_TO_HEAD;
            return false;
        }
        // TODO if validating and testOnBorrow == true then pre-allocate for
        // performance
        return false;
    }

最后再来看下returnObject流程:

  public void returnObject(final T obj) {
        // 获取归还对象对应的PooledObject
        final PooledObject<T> p = allObjects.get(new IdentityWrapper<>(obj));

        if (p == null) {
            if (!isAbandonedConfig()) {
                throw new IllegalStateException(
                        "Returned object not currently part of this pool");
            }
            return; // Object was abandoned and removed
        }
        // 确保对象是Allocated状态,并将对象设置为归还中
        markReturningState(p);

        final long activeTime = p.getActiveTimeMillis();
        
        // 判断对象在归还时是否需要test
        if (getTestOnReturn() && !factory.validateObject(p)) {
            try {
                // 需要验证,但验证不通过则销毁该对象
                destroy(p);
            } catch (final Exception e) {
                swallowException(e);
            }
            try {
                // 如果归还的对象验证没通过,重新创建一个新的idle对象
                ensureIdle(1, false);
            } catch (final Exception e) {
                swallowException(e);
            }
            updateStatsReturn(activeTime);
            return;
        }

        try {
            // 挂起对象,JedisFactory这个方法do Nothing
            factory.passivateObject(p);
        } catch (final Exception e1) {
            swallowException(e1);
            try {
                destroy(p);
            } catch (final Exception e) {
                swallowException(e);
            }
            try {
                ensureIdle(1, false);
            } catch (final Exception e) {
                swallowException(e);
            }
            updateStatsReturn(activeTime);
            return;
        }
        
        // 将对象状态设为idle
        if (!p.deallocate()) {
            throw new IllegalStateException(
                    "Object has already been returned to this pool or is invalid");
        }

        final int maxIdleSave = getMaxIdle();
        if (isClosed() || maxIdleSave > -1 && maxIdleSave <= idleObjects.size()) {
            // 如果对象池关闭了或者超过容量了 直接销毁对象
            try {
                destroy(p);
            } catch (final Exception e) {
                swallowException(e);
            }
        } else {
            // 空闲对象入队 判断是那种队列 先进先出 还是 先进后出
            if (getLifo()) {
                idleObjects.addFirst(p);
            } else {
                idleObjects.addLast(p);
            }
            if (isClosed()) {
                // Pool closed while object was being added to idle objects.
                // Make sure the returned object is destroyed rather than left
                // in the idle object pool (which would effectively be a leak)
                // 对象池关闭了 销毁所有对象
                clear();
            }
        }
        // 更新相关统计信息
        updateStatsReturn(activeTime);
    }
2.3 ObjectPoolFactory

      ObjectPoolFactory 用于大量生成相同类型和设置的ObjectPool,使用工厂模式来产生ObjectPool.避免每个地方重写一次调用相应构造方法的代码。它是一个在org.apache.commons.pool中定义的接口,定义了一个ObjectPool createPool()方法,

public static void main(String[] args) {  
        Object obj = null;  
        PoolableObjectFactory factory = new PoolableObjectFactorySample();  
        ObjectPoolFactory poolFactory = new StackObjectPoolFactory(factory);  
        ObjectPool pool = poolFactory.createPool();  
        try {  
            for(long i = 0; i < 100 ; i++) {  
                System.out.println("== " + i + " ==");  
                obj = pool.borrowObject();  
                System.out.println(obj);  
                pool.returnObject(obj);  
            }  
            obj = null;  
        }  
        catch (Exception e) {  
            e.printStackTrace();  
        }  
        finally {  
            try{  
                if (obj != null) {  
                    pool.returnObject(obj);  
                }  
                pool.close();  
            }  
            catch (Exception e){  
                e.printStackTrace();  
            }  
        }  
    }  

3. 总结

    本文首先介绍了为什么要使用对象池? 然后分析了对象池的基本原理和三大组件PoolableObjectFactory、 ObjectPool、ObjectPoolFactory,其中详细阐述了ObjectPool的一个具体实现类GenericObjectPool及其配置说明。恰当地使用对象池化,可以有效地降低频繁生成某些对象所造成的开销,从而提高整体的性能,而借助Apache Common Pool组件,可以有效地减少Jedis花在处理对象池化上的工作量。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343