jedis對象池原理和源碼分析

1. 什么是對象池稀余?

??????我們都知道一個對象悦冀,比如car其生命周期大致可分為“創(chuàng)建”,“使用”睛琳, “銷毀”三個階段盒蟆,如果每次創(chuàng)建、使用完一個對象就釋放掉师骗,等到需要新的的對象再重新創(chuàng)建历等,當創(chuàng)建一個對象的成本比較大時(比如數(shù)據(jù)庫連接等),這樣會非常消耗資源辟癌。為了節(jié)約系統(tǒng)資源寒屯,就需要就這些之前創(chuàng)建過的對象保存下來,等待下次需要時直接使用愿待,這種用于充當保存對象的“容器”就是對象池浩螺,它使得對象可復(fù)用靴患,減少頻繁創(chuàng)建造成的開銷。

2. 對象池原理

?????試想如果讓你來實現(xiàn)一個對象池要出,應(yīng)該怎么做呢鸳君? 首先,對象池肯定是用來管理對象的患蹂,舉個例子或颊,我們可以把對象看成是一臺公共自行車,將對象池比作一個自行車站點传于,首先囱挑,對于這個站點的功能,肯定有‘借’自行車沼溜,“還”自行車平挑,定期“檢查”自行車車況,“銷毀”(壞了的系草,拿去修或者銷毀)這幾個基本的功能通熄。但站點一般只負責管理車,不會自己造自行車找都,還需要一個自行車工廠去生產(chǎn)車唇辨,而這個造自行車的工廠除了生產(chǎn)車,還需要在對車進行“檢測”能耻、“出庫”功能赏枚、“回收”等功能。
????? jedis使用的pool是apache實現(xiàn)的對象池晓猛,它是否和我們想象的一樣饿幅?大同小異!戒职!在其pool組件中诫睬,對象池化工作被劃分了三類對象: PooledObjectFactory, ObjectPool, ObjectPoolFactory帕涌。

2.1 PooledObjectFactory

?????PooledObjectFactory用于管理被池化的對象的產(chǎn)生摄凡、激活、掛起蚓曼、校驗和銷毀亲澡,它是一個接口,使用者根據(jù)是什么工廠來具體實現(xiàn)纫版。

public interface PooledObjectFactory<T> {

  // 用于產(chǎn)生新的對象
  PooledObject<T> makeObject() throws Exception;
  // 用于銷毀被validateObject判定為已失效的對象
  void destroyObject(PooledObject<T> p) throws Exception;

 // 用于校驗一個具體的對象是否仍然有效床绪,如果對象失效會被destroyObject 方法銷毀
  boolean validateObject(PooledObject<T> p);

 //  將對象重新設(shè)置為初始狀態(tài)
  void activateObject(PooledObject<T> p) throws Exception;
// 將這個對象掛起,設(shè)置為休眠狀態(tài)
  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包中定義的一個接口癞己,它有多種實現(xiàn),GenericObjectPool是其中的一種膀斋,而jedis使用的對象池就是這個pool。
一個對象池往往需要配置很多合適的參數(shù)才能使用痹雅,對GenericObjectPool的配置是通過org.apache.commons.pool.impl.GenericObjectPool.Config來完成仰担,這是個簡單的數(shù)值對象,每個成員都預(yù)設(shè)了默認值绩社,這里我們詳細介紹一下里面的各個成員的含義摔蓝。

2.1 GenericObjectPool 配置詳情
  • maxActive 控制池中對象的最大數(shù)量。默認值是8愉耙,如果是負值表示沒限制贮尉。
  • maxIdle 控制池中空閑的對象的最大數(shù)量。默認值是8朴沿,如果是負值表示沒限制猜谚。
  • minIdle
    控制池中空閑的對象的最小數(shù)量。默認值是0赌渣。
  • whenExhaustedAction 指定池中對象被消耗完以后的行為龄毡,有下面這些選擇:
WHEN_EXHAUSTED_FAIL               0

WHEN_EXHAUSTED_BLOCK              1

????如果是WHEN_EXHAUSTED_FAIL,當池中對象達到上限以后锡垄,繼續(xù)borrowObject會拋出NoSuchElementException異常。
????如果是WHEN_EXHAUSTED_BLOCK祭隔,當池中對象達到上限以后货岭,會一直等待,直到有一個對象可用疾渴。這個行為還與maxWait有關(guān)千贯,如果maxWait是正數(shù),那么會等待maxWait的毫秒的時間搞坝,超時會拋出NoSuchElementException異常搔谴;如果maxWait為負值,會永久等待桩撮。
????whenExhaustedAction 的默認值是WHEN_EXHAUSTED_BLOCK敦第,maxWait的默認值是-1。

  • maxWaitMillis 如果whenExhaustedAction 是WHEN_EXHAUSTED_BLOCK店量,指定等待的毫秒數(shù)芜果。如果maxWait是正數(shù),那么會等待maxWait的毫秒的時間融师,超時會拋出NoSuchElementException異常右钾;如果maxWait為負值,會永久等待。
  • testOnBorrow 如果testOnBorrow被設(shè)置舀射,pool會在borrowObject返回對象之前使用PoolableObjectFactory的validateObject來驗證這個對象是否有效窘茁,要是對象沒通過驗證,這個對象會被丟棄脆烟,然后重新創(chuàng)造一個新的對象山林。testOnBorrow的默認值是false。
  • testOnReturn 如果testOnReturn被設(shè)置浩淘,pool會在returnObject的時候通過PoolableObjectFactory的validateObject方法驗證對象捌朴,如果對象沒通過驗證,對象會被丟棄张抄,不會被放到池中砂蔽。testOnReturn的默認值是false。
  • testWhileIdle 在檢測空閑對象線程檢測到對象不需要移除時署惯,是否檢測對象的有效性左驾。true是,默認值是false极谊。
    這個設(shè)置僅在timeBetweenEvictionRunsMillis被設(shè)置成正值(>0)的時候才會生效诡右。 testWhileIdle的默認值是false。
  • timeBetweenEvictionRunsMillis 空閑對象檢測線程的執(zhí)行周期轻猖,即多長時候執(zhí)行一次空閑對象檢測帆吻。單位是毫秒數(shù)。如果小于等于0咙边,則不執(zhí)行檢測線程猜煮。默認值是-1;
2.2 代碼講解

只看參數(shù)根本看不出它們都是做什么的,還是得從代碼入手败许,先看其uml圖:


image.png

可以看到GenericObjectPool繼承了BaseGenericObjectPool王带,實現(xiàn)了ObjectPool等接口,使用泛型T來指定緩存對象的類型市殷。BaseGenericObjectPool主要做了一些公共的實現(xiàn)愕撰,GenericObjectPool則是復(fù)寫了一些抽象方法,做具體的實現(xiàn)醋寝。

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());
// 設(shè)置參數(shù)
        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();
    }

可以看到搞挣,構(gòu)造GenericObjectPool的關(guān)鍵參數(shù)是PooledObjectFactory,是對象池的使用者自己需要實現(xiàn)的用于產(chǎn)生池化對象的工廠,對于jedis而言音羞,該工廠就是前面介紹的JedisFactory柿究。可以看出對象池和工廠是解耦的黄选,對象池只負責管理對象蝇摸,至于生產(chǎn)婶肩、激活、銷毀等由具體的工廠提供貌夕。
GenericObjectPool創(chuàng)建了一個雙端隊列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);
    }

在參數(shù)設(shè)置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主要用于創(chuàng)建新的Evictor,然后基于ScheduledThreadPoolExecutor進行線程調(diào)度们童。

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方法主要調(diào)用了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;
                    }
                    // 將當前空閑的連接設(shè)置為淘汰狀態(tài)玉控,如果該連接不是空閑狀態(tài)則重新迭代一個
                    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 {
                         // 根據(jù)淘汰策略判斷是否需要淘汰
                        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 設(shè)置驅(qū)逐線程每次檢測對象的數(shù)量汞扎。
    這個設(shè)置僅在timeBetweenEvictionRunsMillis被設(shè)置成正值(>0)的時候才會生效柠傍。numTestsPerEvictionRun的默認值是3。
  • minEvictableIdleTimeMillis 指定最小的空閑驅(qū)逐的時間間隔(空閑超過指定的時間的對象齐板,會被清除掉)吵瞻。這個設(shè)置僅在timeBetweenEvictionRunsMillis被設(shè)置成正值(>0)的時候才會生效。minEvictableIdleTimeMillis默認值是30分鐘甘磨。
  • softMinEvictableIdleTimeMillis 與minEvictableIdleTimeMillis類似橡羞,也是指定最小的空閑驅(qū)逐的時間間隔(空閑超過指定的時間的對象,會被清除掉)济舆,不過會參考minIdle的值卿泽,只有idle對象的數(shù)量超過minIdle的值,對象才會被清除滋觉。這個設(shè)置僅在timeBetweenEvictionRunsMillis被設(shè)置成正值(>0)的時候才會生效签夭,并且這個配置能被minEvictableIdleTimeMillis配置取代(minEvictableIdleTimeMillis配置項的優(yōu)先級更高)。
    softMinEvictableIdleTimeMillis的默認值是-1椎瘟。
  • lifo pool可以被配置成LIFO隊列(last-in-first-out)或FIFO隊列(first-in-first-out),來指定空閑對象被使用的次序侄旬。lifo的默認值是true肺蔚。
    驅(qū)逐線程采取的淘汰策略是默認策略:
public class DefaultEvictionPolicy<T> implements EvictionPolicy<T> {

    @Override
    public boolean evict(final EvictionConfig config, final PooledObject<T> underTest,
            final int idleCount) {
        // 當對象的空閑時間大于SoftMinEvictableIdleTimeMillis并且實際空閑對象數(shù)目大于配置的空閑對象時會被淘汰
        // 或者時間大約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) {
                // 池里已經(jīng)沒有空閑的對象仇冯,需要創(chuàng)建一個,可能創(chuàng)建成功也可能失敗
                p = create();
                if (p != null) {
                    create = true;
                }
            }
            if (blockWhenExhausted) {
                if (p == null) {
                    // 走到這 說明前面創(chuàng)建失敗了 族操,如果沒有設(shè)置超時時間會調(diào)用take阻塞苛坚,知道有資源
                    if (borrowMaxWaitMillis < 0) {
                        p = idleObjects.takeFirst();
                    } else {
                        // 阻塞調(diào)用 等到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()) { // 判斷當前的狀態(tài)是否是idle比被,若是則設(shè)置為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;
                    }
                }
                // 對象要么從池中取出來泼舱,要么是新創(chuàng)建出來等缀,根據(jù)配置的參數(shù)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;
                        }
                    }
                }
            }
        }
        // 更新一些統(tǒng)計信息
        updateStatsBorrow(p, System.currentTimeMillis() - waitTime);

        return p.getObject();
    }

從borrowObject方法可以看出,對象是采取懶加載的方式進行創(chuàng)建娇昙,一開始對象池并沒有對象尺迂,在從對象池取對象時方才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();
                // 和允許創(chuàng)建的最大對象個數(shù)比較
                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
                        // 當前沒有其他創(chuàng)建對象就已經(jīng)達到了最大容量冒掌,直接返回
                        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.
                        // 如果正在創(chuàng)建的對象數(shù)目不是0噪裕,這些創(chuàng)建可能失敗,所以可以等待一下股毫,重新測試
                        makeObjectCountLock.wait(localMaxWaitTimeMillis);
                    }
                } else {
                    // The pool is not at capacity. Create a new object.
                    // 沒有達到容量就直接創(chuàng)建
                    makeObjectCount++;
                    create = Boolean.TRUE;
                }
            }

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

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

        final PooledObject<T> p;
        try {
            // 工廠創(chuàng)建對象
            p = factory.makeObject();
        } catch (final Throwable e) {
            // 創(chuàng)建失敗就回滾
            createCount.decrementAndGet();
            throw e;
        } finally {
            synchronized (makeObjectCountLock) {
                // 釋放鎖,將正在創(chuàng)建對象的統(tǒng)計數(shù)量-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());
            }
        }
        
        // 增加創(chuàng)建的數(shù)量
        createdCount.incrementAndGet();
        // 對象創(chuàng)建成功后放到allObjects Map中 注意不是idle Map
        allObjects.put(new IdentityWrapper<>(p.getObject()), p);
        return p;
    }

可以看到皇拣,新創(chuàng)建的對象一開始并不是放在idle隊列中严蓖,只有在對象return的時候才返回到idle隊列,新創(chuàng)建的對象再borrowObject時被設(shè)置為allocated狀態(tài)

  public synchronized boolean allocate() {
        if (state == PooledObjectState.IDLE) {
            // 將對象設(shè)置為Allocated狀態(tài)
            state = PooledObjectState.ALLOCATED;
            // 設(shè)置借出時間
            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) {
        // 獲取歸還對象對應(yīng)的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狀態(tài)氧急,并將對象設(shè)置為歸還中
        markReturningState(p);

        final long activeTime = p.getActiveTimeMillis();
        
        // 判斷對象在歸還時是否需要test
        if (getTestOnReturn() && !factory.validateObject(p)) {
            try {
                // 需要驗證颗胡,但驗證不通過則銷毀該對象
                destroy(p);
            } catch (final Exception e) {
                swallowException(e);
            }
            try {
                // 如果歸還的對象驗證沒通過,重新創(chuàng)建一個新的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;
        }
        
        // 將對象狀態(tài)設(shè)為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()) {
            // 如果對象池關(guān)閉了或者超過容量了 直接銷毀對象
            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)
                // 對象池關(guān)閉了 銷毀所有對象
                clear();
            }
        }
        // 更新相關(guān)統(tǒng)計信息
        updateStatsReturn(activeTime);
    }
2.3 ObjectPoolFactory

????? ObjectPoolFactory 用于大量生成相同類型和設(shè)置的ObjectPool毒姨,使用工廠模式來產(chǎn)生ObjectPool.避免每個地方重寫一次調(diào)用相應(yīng)構(gòu)造方法的代碼。它是一個在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. 總結(jié)

????本文首先介紹了為什么要使用對象池? 然后分析了對象池的基本原理和三大組件PoolableObjectFactory嵌纲、 ObjectPool俘枫、ObjectPoolFactory,其中詳細闡述了ObjectPool的一個具體實現(xiàn)類GenericObjectPool及其配置說明逮走。恰當?shù)厥褂脤ο蟪鼗剑梢杂行У亟档皖l繁生成某些對象所造成的開銷,從而提高整體的性能师溅,而借助Apache Common Pool組件茅信,可以有效地減少Jedis花在處理對象池化上的工作量。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末墓臭,一起剝皮案震驚了整個濱河市蘸鲸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌窿锉,老刑警劉巖酌摇,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件膝舅,死亡現(xiàn)場離奇詭異,居然都是意外死亡妙痹,警方通過查閱死者的電腦和手機铸史,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來怯伊,“玉大人琳轿,你說我怎么就攤上這事」⑶郏” “怎么了崭篡?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長吧秕。 經(jīng)常有香客問我琉闪,道長,這世上最難降的妖魔是什么砸彬? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任颠毙,我火速辦了婚禮,結(jié)果婚禮上砂碉,老公的妹妹穿的比我還像新娘蛀蜜。我一直安慰自己,他們只是感情好增蹭,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布滴某。 她就那樣靜靜地躺著,像睡著了一般滋迈。 火紅的嫁衣襯著肌膚如雪霎奢。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天饼灿,我揣著相機與錄音幕侠,去河邊找鬼。 笑死碍彭,一個胖子當著我的面吹牛晤硕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播硕旗,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼窗骑,長吁一口氣:“原來是場噩夢啊……” “哼女责!你這毒婦竟也來了漆枚?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤抵知,失蹤者是張志新(化名)和其女友劉穎墙基,沒想到半個月后软族,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡残制,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年立砸,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片初茶。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡颗祝,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出恼布,到底是詐尸還是另有隱情螺戳,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布折汞,位于F島的核電站倔幼,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏爽待。R本人自食惡果不足惜损同,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望鸟款。 院中可真熱鬧膏燃,春花似錦、人聲如沸欠雌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽富俄。三九已至禁炒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間霍比,已是汗流浹背幕袱。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留悠瞬,地道東北人们豌。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像浅妆,于是被迫代替她去往敵國和親望迎。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354