jedisPool實現原理及源碼分析(1)----對象池的說明

redis的併發競爭問題如何解決?java

  Redis爲單進程單線程模式,採用隊列模式將併發訪問變爲串行訪問。Redis自己沒有鎖的概念,Redis對於多個客戶端鏈接並不存在競爭,可是在Jedis客戶端對Redis進行併發訪問時會發生鏈接超時、數據轉換錯誤、阻塞、客戶端關閉鏈接等問題,這些問題均是因爲客戶端鏈接混亂形成。對此有2種解決方法:

1.客戶端角度,爲保證每一個客戶端間正常有序與Redis進行通訊,對鏈接進行池化,同時對客戶端讀寫Redis操做採用內部鎖synchronized。
2.服務器角度,利用setnx實現鎖。
對於第一種,須要應用程序本身處理資源的同步,可使用的方法比較通俗,可使用synchronized也可使用lock;redis

這裏我要提到的是jedis的池化,即jedisPoolapache

      對象的池化技術:服務器

   咱們都知道一個對象好比car(對象)在其生命週期大體可氛圍"建立","使用","銷燬"三大階段,那麼它的總時間就是T1(建立)+T2(使用)+T3(銷燬),若是建立N個對象都須要這樣的步驟的話是很是耗性能的,就算JVM對垃圾回收機制有優化,但"建立"和"銷燬"多少總會佔用部分資源,那麼咱們就會想可否像常量池那樣,讓對象可複用,從而減小T1和T3所消耗的資源呢?這就引出了咱們今天的內容-----對象池化技術即ObjectPool  (jedis也是一個Object對象,咱們下面先介紹對象池)併發

  官網對對象池的解釋是:dom

  將用過的對象保存起來,等下次須要這種對象的時候再拿出來重複使用,從而在必定程度上減小頻繁建立對象所形成的開銷,用於充當保存對象的"容器"對象,被稱爲"對象池"。性能

      對於沒有狀態的對象,String,在重複使用以前,無需進行任何處理,對於有狀態的對象如StringBuffer,在重複使用以前,須要恢復到同等於生成的狀態,若是恢復不了的話,就只能丟掉,改用建立的實例了。並不是全部對象均可以用來池化,由於維護池也須要開銷,對生成時開銷不大的對象進行池化,它可能下降性能。優化

 

在上述的對對象池的描述來看,在看源碼以前我問了本身一個問題,若是讓你來作對象池你覺的應該注意什麼?spa

  咱們舉個例子,所謂對象池嘛確定是管理對象的,咱們將對象當作是一臺公共自行車,將對象池比做一個自行車站點。那麼咱們來想象着,咱們怎麼能夠設計對象池線程

      首先對於這個站點的功能咱們猜測,確定有"借"自行車,"還"自行車,按期"檢查"自行車車況,"銷燬"(壞了的,拿去修或者銷燬)這幾個基本的功能。
      那對於自行車呢?站點確定不能本身造自行車,確定須要一個自行車工廠去維護自行車,那麼天然咱們想一想這個工廠會有哪些功能

  首先做爲工廠,那麼確定會有"生產"功能,"檢測"功能,"出庫"功能,"銷燬"功能。

 有了上述的假象以後,咱們帶着這些概念,看看真正的代碼是怎麼設計的,是否是跟我想的有點相似,下面咱們就來看源代碼

  PooledObjectFactory、ObjectPool和ObjectPoolFactory 
  在Pool組件中,對象池化的工做被劃分給了三類對象: 

  • PooledObjectFactory用於管理被池化的對象的產生、激活、掛起、校驗和銷燬;
  • ObjectPool用於管理要被池化的對象的借出和歸還,並通知PoolableObjectFactory完成相應的工做;
  • ObjectPoolFactory則用於大量生成相同類型和設置的ObjectPool。

  相應地,使用Pool組件的過程,也大致能夠劃分紅「創立PooledObjectFactory」、「使用ObjectPool」和可選的「利用ObjectPoolFactory」三步。

       PooledObjectFactory

   是一個接口,由於工程能夠多多種工廠,自行車,客車,卡車等等。該接口jdk沒有默認實現,須要本身實現。點開對象的工廠,我驚奇的發現,徹底驗證了個人猜測

  只是在基礎功能的基礎上,多增長了一些其餘的功能罷了。

import org.apache.commons.pool.PoolableObjectFactory;

// 1) 建立一個實現了PoolableObjectFactory接口的類。
public class PoolableObjectFactorySample implements PoolableObjectFactory {

    private static int counter = 0;

    // 2) 爲這個類添加一個Object makeObject()方法。這個方法用於在必要時產生新的對象。
    public Object makeObject() throws Exception {
        Object obj = String.valueOf(counter++);
        System.err.println("Making Object " + obj);
        return obj;
    }

    // 3) 爲這個類添加一個void activateObject(Object obj)方法。這個方法用於將對象「激活」——設置爲適合開始使用的狀態。
    public void activateObject(Object obj) throws Exception {
        System.err.println("Activating Object " + obj);
    }

    // 4) 爲這個類添加一個void passivateObject(Object obj)方法。這個方法用於將對象「掛起」——設置爲適合開始休眠的狀態。
    public void passivateObject(Object obj) throws Exception {
        System.err.println("Passivating Object " + obj);
    }

    // 5) 爲這個類添加一個boolean validateObject(Object obj)方法。這個方法用於校驗一個具體的對象是否仍然有效,已失效的對象會被自動交給destroyObject方法銷燬。
    public boolean validateObject(Object obj) {
        /* 以1/2的機率將對象斷定爲失效 */
        boolean result = (Math.random() > 0.5);
        System.err.println("Validating Object "
                + obj + " : " + result);
        return result;
    }

    // 6) 爲這個類添加一個void destroyObject(Object obj)方法。這個方法用於銷燬被validateObject斷定爲已失效的對象。
    public void destroyObject(Object obj) throws Exception {
        System.err.println("Destroying Object " + obj);
    }

}

 

    ObjectPool

 

  有了合適的PooledObjectFactory以後,即可以開始請出ObjectPool來與之同臺演出了。 
ObjectPool是在org.apache.commons.pool包中定義的一個接口,實際使用的時候也須要利用這個接口的一個具體實現。Pool組件自己包含了若干種現成的ObjectPool實現,

StackObjectPool :

StackObjectPool利用一個java.util.Stack對象來保存對象池裏的對象。這種對象池的特點是: 

    • 能夠爲對象池指定一個初始的參考大小(當空間不夠時會自動增加)。
    • 在對象池已空的時候,調用它的borrowObject方法,會自動返回新建立的實例。
    • 能夠爲對象池指定一個可保存的對象數目的上限。達到這個上限以後,再向池裏送回的對象會被自動送去回收。

SoftReferenceObjectPool 

SoftReferenceObjectPool利用一個java.util.ArrayList對象來保存對象池裏的對象。不過它並不在對象池裏直接保存對象自己,而是保存它們的「軟引用」(Soft Reference)。這種對象池的特點是: 

    • 能夠保存任意多個對象,不會有容量已滿的狀況發生。
    • 在對象池已空的時候,調用它的borrowObject方法,會自動返回新建立的實例。
    • 能夠在初始化同時,在池內預先建立必定量的對象。
    • 當內存不足的時候,池中的對象能夠被Java虛擬機回收。

GenericObjectPool----jedis就是基於這個的

GenericObjectPool利用一個org.apache.commons.collections.CursorableLinkedList對象來保存對象池裏的對象。這種對象池的特點是: 

    • 能夠設定最多能從池中借出多少個對象。
    • 能夠設定池中最多能保存多少個對象。
    • 能夠設定在池中已無對象可借的狀況下,調用它的borrowObject方法時的行爲,是等待、建立新的實例仍是拋出異常。
    • 能夠分別設定對象借出和還回時,是否進行有效性檢查。
    • 能夠設定是否使用一個單獨的線程,對池內對象進行後臺清理。

 

 

能夠直接利用。若是都不合用,也能夠根據狀況自行建立。具體的建立方法,能夠參看Pool組件的文檔和源碼。

  我這邊總結了下,對代碼作了一個簡化的修改

  咱們能夠看到,這個對象池就跟咱們以前猜測的站點的功能同樣,主要的方法就是一個借borrowObject(),還returnObject()功能。

 

import org.apache.commons.pool.ObjectPool;
import org.apache.commons.pool.PoolableObjectFactory;
import org.apache.commons.pool.impl.StackObjectPool;

public class ObjectPoolSample {

    public static void main(String[] args) {
        Object obj = null;

        // 1) 生成一個要用的PoolableObjectFactory類的實例。
        PoolableObjectFactory factory = new PoolableObjectFactorySample();

        // 2) 利用這個PoolableObjectFactory實例爲參數,生成一個實現了ObjectPool接口的類(例如StackObjectPool)的實例,做爲對象池。
        ObjectPool pool = new StackObjectPool(factory);

        try {
            for(long i = 0; i < 100 ; i++) {
                System.out.println("== " + i + " ==");

                // 3) 須要從對象池中取出對象時,調用該對象池的Object borrowObject()方法。
                obj = pool.borrowObject();

                System.out.println(obj);

                // 4) 須要將對象放回對象池中時,調用該對象池的void returnObject(Object obj)方法。
                pool.returnObject(obj);

            }
            obj = null;//明確地設爲null,做爲對象已歸還的標誌
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            try{
                if (obj != null) {//避免將一個對象歸還兩次
                    pool.returnObject(obj);
                }

                // 5) 當再也不須要使用一個對象池時,調用該對象池的void close()方法,釋放它所佔據的資源。
                pool.close();

            }
            catch (Exception e){
                e.printStackTrace();
            }
        }
    }

}

 

 ObjectPoolFactory
有時候,要在多處生成類型和設置都相同的ObjectPool。若是在每一個地方都重寫一次調用相應構造方法的代碼,不但比較麻煩,並且往後修改起來,也有所不便。這種時候,正是使用ObjectPoolFactory的時機。 
ObjectPoolFactory是一個在org.apache.commons.pool中定義的接口,它定義了一個稱爲ObjectPool createPool()方法,能夠用於大量生產類型和設置都相同的ObjectPool。 

 

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();  
            }  
        }  
    }  

 

 

 

結束語 恰當地使用對象池化,能夠有效地下降頻繁生成某些對象所形成的開銷,從而提升總體的性能。而藉助Apache Commons Pool組件,能夠有效地減小花在處理對象池化上的工做量,進而,向其它重要的工做裏,投入更多的時間和精力。 

相關文章
相關標籤/搜索