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组件,能够有效地减小花在处理对象池化上的工做量,进而,向其它重要的工做里,投入更多的时间和精力。 

相关文章
相关标签/搜索