当前位置 : 首页 » 文章分类 :  开发  »  Apache-Commons-Pool 使用笔记

Apache-Commons-Pool 使用笔记

Apache Commons Pool 使用笔记

Apache Commons Pool
https://commons.apache.org/proper/commons-pool/

Commons Pool 组件提供了一整套用于实现对象池化的框架,以及若干种各具特色的对象池实现,可以有效地减少处理对象池化时的工作量
commons-pool 是一个通用的池设计,其它框架可以基于commons-pool增加池的功能,它提供了池API和一些默认实现


概述

池化机制的好处

说到池我们可以联想到很多概念,比如线程池、数据库连接池、常量池等等。
在多线程设计中,可以通过池化机制复用对象以达到提高性能的目的,池的核心优势是对 对象 的复用,这样就免去了“对象重复创建”的时间,尤其当创建对象本身是一个耗时的事情,比如IO操作。

拿我们最常见的线程池为例,线程这个对象是可以被复用的,程序要执行的是任务,这些任务可以交给复用的线程来处理,而线程的创建恰恰又是一个比较耗时的操作,我们通过线程对象的池化技术达到复用线程的目的。


实现一个池要考虑哪些因素?

设计一个池的核心要素无非分成两个方面:池子 和 对象。

池子 要考虑大小,池子过大可能会占用过多的系统性能,池子过小可能由于无法获取对象而阻塞线程。当我们很确定自己需要多大的池来执行,可以使用 fixed 大小的池子,我们也可以设计一个对象个数动态变化的池子:池子有一个最大值 maxTotal 和最小值 minIdle ,最大值是对象个数的上限,当池子一段时间没有使用后,就去回收超过最小值个数的对象,这样在系统繁忙时,就可以充分复用对象,在系统空闲时,又可以释放不必要的对象。

对象 必须是可以被复用的,且创建对象应该是耗时的才值得被复用。可以被复用是一个特别重要的点,当你的对象做了一些不可复用的操作后,必须在放回池中的时候重置这些设置,或者说从池子中取出对象时,都要重新进行初始化。


minIdle/maxIdle/maxTotal

maxTotal int: 池子空间最大值,负数表示不设上限,别名:maxActive
maxIdle int: 最大的空闲对象个数,超过此值的对象放回池子的时候都会被释放
minIdle int: 最小的空闲对象个数,无论对象如何被释放,保证池子里面最少的对象个数。minIdle和许多池配置参数 initialSize 初始化大小在实现含义中有着异曲同工之用。类似Java线程池中的coreSize


Commons Pool 的3个核心概念

Commons Pool 有 3 个核心概念: 对象池 ObjectPool, 池化对象 PooledObject, 对象工厂 PooledObjectFactory
ObjectPool 实现对对象存取和状态管理的池实现;如:线程池、数据库连接池
PooledObject 池化对象,是需要放到 ObjectPool 对象的一个包装类。添加了一些附加的信息,比如说状态信息,创建时间,激活时间,关闭时间等
PooledObjectFactory 工厂类,负责生产池化对象,负责具体对象的创建、初始化,对象状态的销毁和验证


PooledObject 池化对象接口

池子中存储的对象不是你需要复用的对象,而是做了一层封装,抽象接口是 PooledObject,为了便于构造这个封装,提供了默认包装类 DefaultPooledObject

package org.apache.commons.pool2;

import java.io.PrintWriter;
import java.util.Deque;

public interface PooledObject<T> extends Comparable<PooledObject<T>> {
    T getObject();
    long getCreateTime();
    long getActiveTimeMillis();
    long getIdleTimeMillis();
    long getLastBorrowTime();
    long getLastReturnTime();
    long getLastUsedTime();

    @Override
    int compareTo(PooledObject<T> other);

    @Override
    boolean equals(Object obj);

    @Override
    int hashCode();

    @Override
    String toString();

    boolean startEvictionTest();

    boolean endEvictionTest(Deque<PooledObject<T>> idleQueue);

    boolean allocate();

    boolean deallocate();

    void invalidate();

    void setLogAbandoned(boolean logAbandoned);

    void use();

    void printStackTrace(PrintWriter writer);

    PooledObjectState getState();

    void markAbandoned();

    void markReturning();
}

PooledObjectFactory 对象工厂接口

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() 定义校验对象的方式
activateObject() 初始化对象
passivateObject() 重置对象

这个接口是需要用户自己去实现的


ObjectPool 对象池接口

package org.apache.commons.pool2;

import java.util.NoSuchElementException;

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

borrowObject()

T borrowObject() throws Exception, NoSuchElementException, IllegalStateException;
从池中获取一个实例
返回的对象是通过 PooledObjectFactory.makeObject() 创建好的对象,或用完后归还的空闲对象。
借走的对象必须通过 returnObject(T)invalidateObject(T) 归还

returnObject(T)

void returnObject(T obj) throws Exception;
把一个对象实例归还给池子,此对象必须是 borrowObject() 借出来的

invalidateObject(T obj)

void invalidateObject(T obj) throws Exception;
使一个对象变为无效,这个对象必须是 borrowObject() 借出来的
如果程序决定一个对象没用了,可以将其设为无效,从池中删除。


使用样例代码

Object obj = null;

try {
    obj = pool.borrowObject();
    try {
        //...use the object...
    } catch(Exception e) {
        // invalidate the object
        pool.invalidateObject(obj);
        // do not return the object to the pool twice
        obj = null;
    } finally {
        // make sure the object is returned to the pool
        if(null != obj) {
            pool.returnObject(obj);
       }
    }
} catch(Exception e) {
      // failed to borrow an object
}

GenericObjectPool 使用示例

GenericObjectPool 是一个默认池子的实现,创建的时候需要提供一个对象工厂 PooledObjectFactory, 然后通过 borrowObject() 借取对象,returnObject() 归还对象
应用要做的所有事情就是去定义如何生成对象等操作,即实现 BasePooledObjectFactory ,这是一个抽象实现,更通用的接口是 PooledObjectFactory<T>

public class CommonsPoolTest {
    @Test
    public void test() throws Exception {
        GenericObjectPool<StringBuffer> pool = new GenericObjectPool<StringBuffer>(new BasePooledObjectFactory<StringBuffer>() {
            @Override
            public StringBuffer create() throws Exception {
                return new StringBuffer();
            }

            @Override
            public PooledObject<StringBuffer> wrap(StringBuffer buffer) {
                return new DefaultPooledObject<StringBuffer>(buffer);
            }

            @Override
            public void passivateObject(PooledObject<StringBuffer> pooledObject) {
                pooledObject.getObject().setLength(0);
            }
        });

        pool.setMaxTotal(8);
        pool.setMaxIdle(4);
        pool.setMinIdle(2);
        printPoolStatus(pool);
        System.out.println("pool 初始化完成\n");

        StringBuffer[] buffer = new StringBuffer[9];

        for (int i = 0; i < 8; i++) {
            System.out.print("borrow " + i);
            buffer[i] = pool.borrowObject();
            buffer[i].append("string buffer " + i);
            printPoolStatus(pool);
        }
        // 如果 borrow 第 9 个,会阻塞
//        buffer[8] = pool.borrowObject();
        for (int i = 0; i < 8; i++) {
            System.out.print("return " + i);
            pool.returnObject(buffer[i]);
            printPoolStatus(pool);
        }
    }

    private void printPoolStatus(GenericObjectPool pool) {
        System.out.println(" [active: " + pool.getNumActive() + ", idle: " + pool.getNumIdle() + ", wait: " + pool.getNumWaiters() + "]");
    }
}

结果

[active: 0, idle: 0, wait: 0]
pool 初始化完成

borrow 0 [active: 1, idle: 0, wait: 0]
borrow 1 [active: 2, idle: 0, wait: 0]
borrow 2 [active: 3, idle: 0, wait: 0]
borrow 3 [active: 4, idle: 0, wait: 0]
borrow 4 [active: 5, idle: 0, wait: 0]
borrow 5 [active: 6, idle: 0, wait: 0]
borrow 6 [active: 7, idle: 0, wait: 0]
borrow 7 [active: 8, idle: 0, wait: 0]
return 0 [active: 7, idle: 1, wait: 0]
return 1 [active: 6, idle: 2, wait: 0]
return 2 [active: 5, idle: 3, wait: 0]
return 3 [active: 4, idle: 4, wait: 0]
return 4 [active: 3, idle: 4, wait: 0]
return 5 [active: 2, idle: 4, wait: 0]
return 6 [active: 1, idle: 4, wait: 0]
return 7 [active: 0, idle: 4, wait: 0]

可以看到,由于设置了 maxIdle 是 4,当空闲对象多余4个时会被回收,以保证最大空闲个数是 4


GenericObjectPool池的实现原理

GenericObjectPool 内部通过阻塞队列维护一个空闲对象集合 idleObjects,通过一个 Map 维护所有对象的映射 allObjects

private final Map<IdentityWrapper<T>, PooledObject<T>> allObjects = new ConcurrentHashMap<IdentityWrapper<T>, PooledObject<T>>();
private final LinkedBlockingDeque<PooledObject<T>> idleObjects;

borrow的逻辑
判断池子是否已满是否需要阻塞,如果 idleObjects 中有空闲对象则直接取空闲对象,否则通过传入的 PooledObjectFactory 新创建一个对象,放入 allObjects map 并返回。

return的逻辑
首先判断 归还的实例是否池子中的示例,也就是是否在 allObjects 这个 map 中, 是的话重置对象,当 空闲实例个数 超过最大空闲值 maxIdle 时就会销毁这个对象,从 allObjects 和 idleObjects 中删除,并调用 PooledObjectFactory 的 destroyObject() 方法销毁对象

对象池化机制(一)池设计和commons-pool原理
https://github.com/Sayi/sayi.github.com/issues/64

对象池化机制(二)数据库连接池
https://github.com/Sayi/sayi.github.com/issues/66


JedisPool 中使用了Commons-Pool

JedisPool 中使用了 Commons-Pool 中的 GenericObjectPool

Apache Commons-pool2(整理)
https://www.jianshu.com/p/b0189e01de35

Apache Common Pool2 对象池应用浅析
https://zhuanlan.zhihu.com/p/36216932


上一篇 Lombok

下一篇 Git-WebHooks钩子

阅读
评论
2,144
阅读预计9分钟
创建日期 2019-09-01
修改日期 2020-04-10
类别

页面信息

location:
protocol:
host:
hostname:
origin:
pathname:
href:
document:
referrer:
navigator:
platform:
userAgent:

评论