概览
Apache Commons Pool开源软件库是对象池技术的一种具体实现,提供了一个对象池API和许多对象池实现。简单的说就是将对象一次创建多次使用,解决频繁的创建和销毁对象带来的性能损耗问题。
文档
在线 API 文档:Javadoc (2.5.0 release)
使用
下载:downloads Maven :
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.5.0</version>
</dependency>
包
整个项目有三个包
包 | 介绍 |
---|---|
org.apache.commons.pool2 | 对象池API |
org.apache.commons.pool2.impl | 对象池API实现。 |
org.apache.commons.pool2.proxy | 对象池代理实现。 |
原理解析
Common-pool2由三大模块组成
对象池(ObjectPool):一种容器,维护“对象列表”的存取,包含池对象工厂
池对象(PooledObject):是对需要放到池里对象的一个包装类,添加了一些附加的信息
池对象工厂(PooledObjectFactory):创建池对象的工厂类,提供池对象的创建、初始化、销毁和验证等功能
即PooledObjectFactory创建PooledObject,放到ObjectPool里管理
1.对象池
使用对象池的一般步骤:创建一个池对象工厂,将该工厂注入到对象池中,当要取池对象,调用borrowObject,当要归还池对象时,调用returnObject,销毁池对象调用clear(),如果要连池对象工厂也一起销毁,则调用close()。
1.1 默认实现
ObjectPool类族中最主要的两类接口是ObjectPool和KeyedObjectPool,具体的实现里有5种:
- GenericObjectPool和GenericKeyedObjectPool:可配置LIFO/FIFO。默认采用LIFO。
- SoftReferenceObjectPool:使用LIFO行为实现的ObjectPool。一次性创建所有池化对象,并对容器中的每个对象进行软引用(SoftReference)处理,允许垃圾收集器根据需要从池中逐出空闲实例。
- ProxiedObjectPool和ProxiedKeyedObjectPool:使用CGLIB或者JDK自带动态代理技术,代理由GenericObjectPool、GenericKeyedObjectPool或者SoftReferenceObjectPool产生的ObjectPool对象
KeyedObjectPool基于key操作池对象,不同key的容器不同。 操作方法和ObjectPool的差别是要提供key参数。
用户可以通过继承或者封装默认实现使用线程池,或直接使用默认实现。
1.2 GenericObjectPool
GenericObjectPool实现了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();
}
构造函数需要传入factory和Config:
GenericObjectPool(PooledObjectFactory<T> factory, GenericObjectPoolConfig config)
数据结构: 所有对象allObjects用ConcurrentHashMap存储,空闲对象idleObjects用LinkedBlockingDeque存储。
-
borrowObject() 1 从idleObjects中pollFirst获取空闲对象。
2 若未获取到,检查对象池对象是否达到上限。若否,则调用factory的makeObject去创建一个对象,并放入allObjects中。若是,阻塞直到有对象被归还或阻塞超时报错。
3 得到对象之后,调用factory的activateObject方法激活对象,调用factory的validateObject方法验证对象
4 若验证失败,则调用destroy方法销毁对象;验证通过则返回对象
-
returnObject(Object obj) :
1 根据obj从allObjects拿到其对应的PooledObject p
2 判空;将p状态置为RETURN
3 若getTestOnReturn参数为true,验证对象,若验证不过则销毁对象
4 对p进行passivateObject,钝化对象
5 更新p状态为IDLE
6 归还Pool:Pool的idle实例达到上限或者Pool已经关闭,销毁之,否则将p加入到idleObjects中。
1.3 GenericObjectPoolConfig
GenericObjectPoolConfig是一个配置类,提供了很多参数来控制对象池的相关属性。
GenericObjectPool就是根据GenericObjectPoolConfig的配置信息,使用PooledObjectFactory来维护对象池。
2.对象工厂接口
**池对象工厂(PooledObjectFactory)是创建和管理池对象(PooledObject)**生命周期的通用接口:
public interface PooledObjectFactory<T> {
//创建对象
PooledObject<T> makeObject();
//激活对象,重新初始化,使对象焕然如新。
void activateObject(PooledObject<T> obj);
//钝化对象,让对象置为未初始化状态。
void passivateObject(PooledObject<T> obj);
//验证对象
boolean validateObject(PooledObject<T> obj);
//销毁对象
void destroyObject(PooledObject<T> obj);
}
PooledObjectFactory在包里没有具体实现,因为这涉及到具体对象的创建,需要应用本身去实现,这也体现了设计上的解耦合性。
实现PoolableObjectFactory接口的最简单方法是扩展BasePooledObjectFactory抽象类。
和ObjectPool相对应的,PooledObjectFactory也有一个相应的KeyedPooledObjectFactory,他们的区别也一样,一个是基于key来操作一个不是。
3.池对象
PooledObject主要是对需要被加入到pool里的对象提供一个包装,方便来查看或者统计一些对象信息。
PooledObject接口里的基本方法定义
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();
}
impl包中提供了PooledObject的默认实现DefaultPooledObject。要使用DefaultPooledObject封装,请使用
@Override
public PooledObject<Foo> wrap(Foo foo) {
return new DefaultPooledObject<Foo>(foo);
}
实例
apache的连接池工具库common-dbcp是基于common-pool实现的,而Jedis的连接池基于commons-pool2实现。
Apache commons-pool2使用的基本步骤:
步骤一: 实现自己的PooledObjectFactory
有时爬虫经常会用到WebDriver,以实现的PooledObjectFactory管理WebDriver为例,其代码如下:
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.phantomjs.PhantomJSDriver;
/**
* @author wujun
* @date 2018-04-19 16:10
*/
public class PooledWebDriverFactory implements PooledObjectFactory<WebDriver> {
@Override
public PooledObject<WebDriver> makeObject() throws Exception {
WebDriver webDriver = new PhantomJSDriver();
return new DefaultPooledObject<>(webDriver);
}
@Override
public void destroyObject(PooledObject<WebDriver> p) throws Exception {
WebDriver webDriver = p.getObject();
if (webDriver != null) {
webDriver.quit();
}
}
@Override
public boolean validateObject(PooledObject<WebDriver> p) {
return p.getObject() != null;
}
@Override
public void activateObject(PooledObject<WebDriver> p) throws Exception {
if (null == p.getObject()) {
p = makeObject();
}
}
@Override
public void passivateObject(PooledObject<WebDriver> p) throws Exception {
}
}
步骤二: 创建ObjectPool对象
可直接使用GenericObjectPool,若第二个参数GenericObjectPoolConfig不传则会使用默认config。也可继承或者封装GenericObjectPool完成自己的需求。
ObjectPool op = new GenericObjectPool<WebDriver>(new PooledWebDriverFactory());
步骤三: 从ObjectPool获取到PooledObject对象,进行相关业务操作
ObjectPool op = new GenericObjectPool<WebDriver>(new PooledWebDriverFactory());
WebDriver webDriver = (WebDriver) op.borrowObject();
webDriver.get(url);
op.returnObject(webDriver);
参考
Apache commons-pool对象池原理分析
开源项目剖析之apache-common-pool
Apache commons-pool2-2.4.2源码学习笔记
Apache Common-pool2对象池分析和应用
Apache common-pool,common-dbcp源码解读与对象池原理剖析