common-pool对象池

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/liyantianmin/article/details/86591742

简介

    在以往的一些工作中总接触到一个池的概念。这些池包括有线程池,对象池和连接池等。从池本身的概念来说,它是将一系列的资源事先准备好放在一个地方,等需要的时候直接拿过去用。而用完之后再放回来。和我们平常的需要使用资源再创建的方式相比,这种池的方式节省了创建和销毁资源的这么一个过程。所以说,对于一些比较比较稀缺的资源或者创建和销毁影响系统性能的资源,采用池的方式可以有效的提高整体性能。最近一段时间正好要用到一些数据库连接池等东西,趁这个时机把一些相关的部分好好的学习总结一下。如前面提到的,我们使用到的典型的线程池有jdk里的ExecutorService相关的一系列工具类(在这篇文章里有阐述),而一些典型的数据库连接池有cp03和dbcp。最近用到了dbcp,而它主要是基于commons-pool实现的,所以我们先从commons-pool2开始吧。

整体结构分析

    在正式分析commons-pool2的结构开始,我们设想一下,如果我们自己来设计一个object pool的话,我们该怎么来做呢?

    首先一个,我们从直接使用的角度来说,肯定需要一个ObjectPool对象,它负责保存我们需要访问的对象,我们把这个对象用完之后就返还给它。更细化一点的考虑的话,我们在使用前需要根据配置事先创建一些对象,后面需要使用的时候直接取就可以了。然后我们需要能够知道pool里面有多少可以用的对象。还有一个就是对象在被使用前是一个类似于初始化的状态,而使用完之后可能是另外的一个状态,既然我们希望对象能够被复用,在返回到pool里的时候可能还需要做一些重置的工作。

    除了这些以外,如果我们考虑对象的创建。我们可以将创建对象的职责给pool,这样pool同时既要管理对象的存取和回收又要管对象的创建和状态设置等。这有点像一个图书馆的管理员,他既要管借还书,还要管怎么造纸印书。这样想来,似乎管的职责有点多了。我们也可以将对象的创建单独分离处理作为一个factory来做。

    其实,通过前面的这些讨论,我们几乎也能创建一个像模像样的pool。如果结合commons-pool2的详细设计,我们会发现它的设计思路和我们也是基本上一致的。它主要由3个大的部分组成:

ObjectPool:专门实现对象存取和状态管理的池实现。我们直接操作的线程池就是定义在这里。值得注意的一点是这里定义的只是怎么来获取以及释放对象等操作,至于具体对象是怎么创建的,一般都通过独立的一个PooledObjectFactory来操心了。

PooledObject:这是commons-pools里比较有意思的一个类族。是对需要放到池里对象的一个包装类。添加了一些附加的信息,比如说状态信息,创建时间,激活时间,关闭时间等。这些添加的信息方便pool来管理和实现一些特定的操作。

PooledObjectFactory: 如我们前面所讨论的,管理具体对象的状态,比如创建,初始化,验证对象状态和销毁对象。

通过我们的这些讨论,他们这三者就构成了一个object pool的基本框架。他们的关系可以用如下的一个图来描述:

    用一句话来概括他们整体的关系就是factory创建需要放入pool的对象,经过PooledObject包装一下就可以上架了。

    有一个地方需要注意一下,虽然我们这里将它划分成3个主要的部分,从更精确的细节来看,每一个类族里都有一些不同的实现,这里我们再针对每一类族做一个大体的介绍。

public interface PooledObjectFactory<T> {
    PooledObject<T> makeObject();  //新增对象
    void activateObject(PooledObject<T> obj);//用于从pool中借出去之前检测该对象是否处于钝化状态,
    void passivateObject(PooledObject<T> obj);//当对象被归还到池中之前执行
    boolean validateObject(PooledObject<T> obj);//在他从池中借出的之前检测对象是否可用
    void destroyObject(PooledObject<T> obj);//销毁对象
}

PooledObjectFactory必须是线程安全的,ObjectPool承诺不会将同一个object实例传递给PooledObjectFactory的多个方法。 
PoolObjectFactory创建和管理PoolObject,这些PoolObject用于作为包装类包装我们要缓存的对象,包装类维持了缓存对象的状态,包装类有DefaultPooledObject,或者实现PoolableObjectFactory接口,或者抽象类BasePooledObjectFactory抽象类,包装类提供了makeObject()方法返回wrap(create()),其中wrap()和create()方法都是抽象方法,需要使用者自己扩展。 
BaseKeyedPooledObjectFactory是实现KeyedPooledObjectFactory接口。 
GenericObjectPool实现了ObjectPool接口,提供了丰富的缓存池操作。

ObjectPool类族

    ObjectPool类族包含了以ObjectPool为代表的一系列pool,其中最主要的两类就是ObjectPool和KeyedObjectPool,他们分别针对普通的pool和以名值对映射的pool。和他们相关的整体几个类结构如下图:

    从前面的图里头我们可以看到,真正定义的接口就是ObjectPool和KeyedObjectPool,具体的实现里有GenericObjectPool, GenericKeyedObjectPool和SoftReferenceObjectPool。不管是哪一种他们都引用了PooledObject。 这里我们不详细分析具体实现的代码,我们先就前面的设想来验证一下他们的整体思路。既然是ObjectPool,那它就应该管borrowObject, returnObject之类的了。是不是呢?我们先看看ObjectPool这一块的代码:

public interface ObjectPool<T> {  
    T borrowObject() throws Exception, NoSuchElementException,  
            IllegalStateException;  
  
    void returnObject(T obj) throws Exception;  
  
    void invalidateObject(T obj) throws Exception;  
  
    void addObject() throws Exception, IllegalStateException,  
            UnsupportedOperationException;  
  
    int getNumIdle();  
  
    int getNumActive();  
  
    void clear() throws Exception, UnsupportedOperationException;  
  
    void close();  
}  

这部分的代码很简单直接,主要就是包含了一个pool需要的基本操作功能,比如从pool里取对象的borrowObject,返回对象到pool里的returnObject,还有一些查找里面可用对象数量以及管理pool的方法。这里的clear方法是用来清空里面处于idle状态的对象,而close方法则用来关闭整个pool。他们两者的一个典型差别就是clear之后里面没有可以用的对象了,我们需要再创建一些对象放进去。而close之后则连pool都访问不了了。

    与ObjectPool对象的一个是KeyedObjectPool,它的操作方法基本上和ObjectPool一样,唯一的差别是它是基于key来操作的,所以所有相关的borrowObject, returnObject等操作都要提供key参数。

3种对象池


在org.apache.commons.pool2.impl中预设了三个可以直接使用的对象池:GenericObjectPool、GenericKeyedObjectPool和SoftReferenceObjectPool。

通常使用GenericObjectPool来创建对象池,如果是对象池是Keyed的,那么可以使用GenericKeyedObjectPool来创建对象池。这两个类都提供了丰富的配置选项。这两个对象池的特点是可以设置对象池中的对象特征,包括LIFO(后进先出)方式、最大空闲数、最小空闲数、是否有效性检查等等。两者的区别如前面所述,后者支持Keyed。

而SoftReferenceObjectPool对象池,它利用一个java.util.ArrayList对象来保存对象池里的对象。不过它并不在对象池里直接保存对象本身,而是保存它们的“软引用”

(Soft Reference)。这种对象池的特色是:可以保存任意多个对象,不会有容量已满的情况发生;在对象池已空的时候,调用它的borrowObject方法,会自动返回新创建的实例;可以在初始化同时,在池内预先创建一定量的对象;当内存不足的时候,池中的对象可以被Java虚拟机回收。

PooledObject类族

    如前所述,PooledObject主要是对需要被加入到pool里的对象提供一个包装,方便来查看或者统计一些对象信息,比如某个对象创建的时间,空闲时间以及活跃时间等。下面是PooledObject相关的类关系图:

PooledObjectFactory

    在commons-pool2里,我们专门抽象出来一个创建和销毁对象的接口,PooledObjectFactory。而具体对象的创建则由用户自定义实现。在本身的源代码里只有一个简单的抽象类BasePooledObjectFactory实现。我们来看看这个接口定义的方法:

public interface PooledObjectFactory<T> {  
  
  PooledObject<T> makeObject() throws Exception;  
  
  void destroyObject(PooledObject<T> p) throws Exception;  
  
  boolean validateObject(PooledObject<T> p);  
  
  void activateObject(PooledObject<T> p) throws Exception;  
  
  void passivateObject(PooledObject<T> p) throws Exception;  
}  

makeObject和destroyObject则分别用于创建销毁对象,很简单。而validateObject用于在每次创建对象放入到pool里或者从pool里取对象出来的时候验证是否对象合法。至于activeObject和passivateObject比较有意思。在这里给对象定义了一系列的状态。这里的设计假定所有在pool里可以被立马拿出去用的对象是一个idle状态,在拿到之后要用activateObject方法来激活一下。而对象要返回给pool的时候则要用passivateObject方法将它钝化。

    和ObjectPool相对应的,PooledObjectFactory也有一个相应的KeyedPooledObjectFactory,他们的区别也一样,一个是基于key来操作一个不是。

其他

    除了我们前面列举出来的这3个部分,还有org.apache.commons.pool2.impl和org.apache.commons.pool2.proxy这两个包。他们分别定义了前面定义的pool的默认实现和基于代理的实现。在后续的文章里会针对每个部分的细节实现做一个分析。

总结

    commons-pool2的整体结构并不复杂,可以将其划分为3个角色,一个管对象的进出口,一个管对象的包装,一个对象的创建。感觉好像一个产销结合的商业团体啊。有意思。

实例

public class ObjectPoolManager {
    private static GenericKeyedObjectPool<String,UserInfoObj> pool = null;

    static {
        //配置项
        GenericKeyedObjectPoolConfig config = new GenericKeyedObjectPoolConfig();
        config.setMaxTotal(5);
        config.setMaxIdlePerKey(5);
        pool = new GenericKeyedObjectPool(new MyKeyedPooledObjectFactory(),config);
    }

    public static GenericKeyedObjectPool<String,UserInfoObj> getPool(){
        return pool;
    }

    static class MyKeyedPooledObjectFactory extends BaseKeyedPooledObjectFactory<String, UserInfoObj> {

        @Override
        public UserInfoObj create(String s) throws Exception {
            UserInfoObj userInfoObj = new UserInfoObj(s, 122);
            System.out.println("do create:"+s+"=="+userInfoObj.toString());
            return userInfoObj;
        }

        @Override
        public PooledObject<UserInfoObj> wrap(UserInfoObj userInfoObj) {
            return new DefaultPooledObject<>(userInfoObj);
        }
    }
}
public class UserInfoObj {
    private String name;

    private int age;

    public UserInfoObj(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
public class PoolTest {
    public static void main(String[] args) throws Exception {
        //提前加入进去,在后面s2并没有新创建对象
        ObjectPoolManager.getPool().addObject("zs4");
        //对象池中没有zs3,下需要新创建一次
        UserInfoObj zs = ObjectPoolManager.getPool().borrowObject("zs3");
        System.out.println(zs.toString()+"==="+JSON.toJSONString(zs));
        //返还对象
        ObjectPoolManager.getPool().returnObject("zs3",zs);
        //由于sz3返还了对象,所以再次获取时不会触发create调用.直接获取对象
        UserInfoObj zs1 = ObjectPoolManager.getPool().borrowObject("zs3");
        System.out.println(zs1.toString()+"==="+JSON.toJSONString(zs1));
        //提前加入进去,此处没有触发create调用,不会创建对象
        UserInfoObj zs2 = ObjectPoolManager.getPool().borrowObject("zs4",100);
        System.out.println(zs2.toString()+"==="+JSON.toJSONString(zs2));

        //由于s2没有返还对象,所以再次获取zs4这个key的时候需要重新创建一个对象
        UserInfoObj zs3 = ObjectPoolManager.getPool().borrowObject("zs4",100);
        System.out.println(zs3.toString()+"==="+JSON.toJSONString(zs3));
    }
}

运行结果:

do create:zs4==com.commonpool.UserInfoObj@6d5380c2
do create:zs3==com.commonpool.UserInfoObj@108c4c35
com.commonpool.UserInfoObj@108c4c35==={"age":122,"name":"zs3"}
com.commonpool.UserInfoObj@108c4c35==={"age":122,"name":"zs3"}
com.commonpool.UserInfoObj@6d5380c2==={"age":122,"name":"zs4"}
do create:zs4==com.commonpool.UserInfoObj@3ab39c39
com.commonpool.UserInfoObj@3ab39c39==={"age":122,"name":"zs4"}

猜你喜欢

转载自blog.csdn.net/liyantianmin/article/details/86591742
今日推荐