参考
http://blog.csdn.net/kongxx/article/details/6607083 以及之后的两篇
PoolableObjectFactory和ObjectPool
工作中需要减少对一些比较耗系统资源对象的创建和初始化工作,因此想到了apache commons-pool工具包。commons-pool包里主要包括三个重要的接口:
ObjectPool用于管理要被池化的对象的借出和归还;
ObjectPoolFactory用于大量生成相同类型和设置的ObjectPool。
看看下面的例子
一个Connection类,可以想象成一个远程连接比如数据库连接等。其中包括创建连接,关闭连接,和一个print方法。
- package com.googlecode.garbagecan.commons.pool.sample1;
-
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
-
- public class MyConnection {
-
- private static Logger logger = LoggerFactory.getLogger(MyConnection.class);
-
- private String name;
- private boolean connected;
-
- public MyConnection(String name) {
- this.name = name;
- }
-
- public void connect() {
- this.connected = true;
- logger.info(name + ": " + connected);
- }
-
- public void close() {
- this.connected = false;
- logger.info(name + ": " + connected);
- }
-
- public boolean isConnected() {
- return this.connected;
- }
-
- public String getName() {
- return this.name;
- }
-
- public void print() {
- logger.info(this.name);
- }
- }
一个PoolableObjectFactory接口的实现类,提供makeObject, activateObject, passivateObject, validateObject, destroyObject方法。
- package com.googlecode.garbagecan.commons.pool.sample1;
-
- import org.apache.commons.pool.PoolableObjectFactory;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
-
- public class MyConnectionPoolableObjectFactory implements PoolableObjectFactory {
-
- private static Logger logger = LoggerFactory.getLogger(MyConnectionPoolableObjectFactory.class);
-
- private static int count = 0;
-
- public Object makeObject() throws Exception {
- MyConnection myConn = new MyConnection("conn_" + (++count));
- myConn.connect();
- logger.info(myConn.getName());
- return myConn;
- }
-
- public void activateObject(Object obj) throws Exception {
- MyConnection myConn = (MyConnection)obj;
- logger.info(myConn.getName());
- }
-
- public void passivateObject(Object obj) throws Exception {
- MyConnection myConn = (MyConnection)obj;
- logger.info(myConn.getName());
- }
-
- public boolean validateObject(Object obj) {
- MyConnection myConn = (MyConnection)obj;
- logger.info(myConn.getName());
- return myConn.isConnected();
- }
-
- public void destroyObject(Object obj) throws Exception {
- MyConnection myConn = (MyConnection)obj;
- logger.info(myConn.getName());
- myConn.close();
- }
- }
最后是一个测试类
- package com.googlecode.garbagecan.commons.pool.sample1;
-
- import org.apache.commons.pool.ObjectPool;
- import org.apache.commons.pool.PoolableObjectFactory;
- import org.apache.commons.pool.impl.StackObjectPool;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
-
- public class Test {
- private static Logger logger = LoggerFactory.getLogger(Test.class);
-
- public static void main(String[] args) throws Exception {
- PoolableObjectFactory factory = new MyConnectionPoolableObjectFactory();
- ObjectPool pool = new StackObjectPool(factory);
- try {
- logger.info("================================================");
- for (int i = 0; i < 10; i++) {
- MyConnection myConn = (MyConnection)pool.borrowObject();
- try {
- myConn.print();
- } catch(Exception ex) {
- pool.invalidateObject(myConn);
- } finally {
- pool.returnObject(myConn);
- }
- }
-
- logger.info("================================================");
- for (int i = 0; i < 10; i++) {
- MyConnection myConn1 = (MyConnection)pool.borrowObject();
- MyConnection myConn2 = (MyConnection)pool.borrowObject();
- myConn1.print();
- myConn2.print();
- pool.returnObject(myConn1);
- pool.returnObject(myConn2);
- }
- } finally {
- try {
- pool.close();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- }
运行测试类,可以看到在第一个循环里虽然循环了10次,一共要了10个MyConnection对象,但是每次返回的都是“conn_1”这个MyConnection对象实例,并且从日志可以看出,makeObject方法只被调用了一次,因此,除了第一次以外,后面的每次申请都是从pool里取出来的。而在第二个循环中,每次申请了两个MyConnection对象实例,从日志可以看到,在第二个循环里也只调用了一次makeObject方法,并且创建的是conn_2对象实例,这是由于conn_1这个对象已经在第一个循环中被创建了出来,此时只是直接拿出来使用了。这里为了好测试,没有在第二个循环中做异常处理,真实情况下应该像第一个循环里的代码类是,在borrowObject和使用pool中对象出现异常时要记得调用invalidateObject方法,并且归还pool中的对象。
KeyedPoolableObjectFactory和KeyedObjectPool
有时候,仅仅简单的把池中的所有对象都当成一类对象并不能解决所有问题,有时候我们需要根据一些参数比如key值去查找某些指定的池中对象,比如可以根据一个参数来决定使用池中具体的那一个数据库连接,等等。此时就需要使用KeyedPoolableObjectFactory和KeyedObjectPool接口。
一个Connection类,可以想象成一个远程连接比如数据库连接等。其中包括创建连接,关闭连接,和一个print方法。
- package com.googlecode.garbagecan.commons.pool.sample2;
-
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
-
- public class MyConnection {
-
- private static Logger logger = LoggerFactory.getLogger(MyConnection.class);
-
- private String name;
- private boolean connected;
-
- public MyConnection(String name) {
- this.name = name;
- }
-
- public void connect() {
- this.connected = true;
- logger.info(name + ": " + connected);
- }
-
- public void close() {
- this.connected = false;
- logger.info(name + ": " + connected);
- }
-
- public boolean isConnected() {
- return this.connected;
- }
-
- public String getName() {
- return this.name;
- }
-
- public void print() {
- logger.info(this.name);
- }
- }
一个KeyedPoolableObjectFactory接口的实现类,提供makeObject, activateObject, passivateObject, validateObject, destroyObject方法。
- package com.googlecode.garbagecan.commons.pool.sample2;
-
- import org.apache.commons.pool.KeyedPoolableObjectFactory;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
-
- public class MyConnectionKeyedPoolableObjectFactory implements KeyedPoolableObjectFactory {
-
- private static Logger logger = LoggerFactory.getLogger(MyConnectionKeyedPoolableObjectFactory.class);
-
- private static int count = 0;
-
- public Object makeObject(Object key) throws Exception {
- MyConnection myConn = new MyConnection(key.toString());
- logger.info(myConn.getName());
- myConn.connect();
- return myConn;
- }
-
- public void activateObject(Object key, Object obj) throws Exception {
- MyConnection myConn = (MyConnection)obj;
- logger.info(myConn.getName());
- }
-
- public void passivateObject(Object key, Object obj) throws Exception {
- MyConnection myConn = (MyConnection)obj;
- logger.info(myConn.getName());
- }
-
- public boolean validateObject(Object key, Object obj) {
- MyConnection myConn = (MyConnection)obj;
- logger.info(myConn.getName());
- return myConn.isConnected();
- }
-
- public void destroyObject(Object key, Object obj) throws Exception {
- MyConnection myConn = (MyConnection)obj;
- logger.info(myConn.getName());
- myConn.close();
- }
- }
最后是一个测试类
- package com.googlecode.garbagecan.commons.pool.sample2;
-
- import org.apache.commons.pool.KeyedObjectPool;
- import org.apache.commons.pool.impl.StackKeyedObjectPool;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
-
- public class Test {
- private static Logger logger = LoggerFactory.getLogger(Test.class);
-
- public static void main(String[] args) throws Exception {
- MyConnectionKeyedPoolableObjectFactory factory = new MyConnectionKeyedPoolableObjectFactory();
- KeyedObjectPool pool = new StackKeyedObjectPool(factory);
- try {
- logger.info("================================================");
- for (int i = 0; i < 10; i++) {
- String key = "conn_" + i;
- MyConnection myConn = (MyConnection)pool.borrowObject(key);
- try {
- myConn.print();
- } catch(Exception ex) {
- pool.invalidateObject(key, myConn);
- } finally {
- pool.returnObject(key, myConn);
- }
- }
-
- logger.info("================================================");
- for (int i = 0; i < 10; i++) {
- String key = "conn_xxx";
- MyConnection myConn = (MyConnection)pool.borrowObject(key);
- try {
- myConn.print();
- } catch(Exception ex) {
- pool.invalidateObject(key, myConn);
- } finally {
- pool.returnObject(key, myConn);
- }
- }
- } finally {
- logger.info("Close Pool");
- try {
- pool.close();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- }
运行测试类,可以看到在第一个循环里虽然循环了10次,一共要了10次MyConnection对象,每次请求的key都不一样,从conn_0到conn_9,由于使用的是KeyedPoolableObjectFactory和KeyedObjectPool接口,所以返回的10个MyConnection对象实例每个都不一样,并且从日志可以看出makeObject方法被调用了10次。第二个循环里,虽然也是请求了10次MyConnection对象,但是由于每次的key都不一样,所以每次返回的都是同一个MyConnection对象实例,并且从日志可以看出makeObject方法只被调用了一次。
GenericObjectPool和GenericKeyedObjectPool
前面两篇文章说了怎么样简单的使用commons-pool库,这里需要考虑一个问题就是在很多时候我们在池里的对象都是比较重型的并且大多数比较稀缺的资源,比如说数据库连接,这样如果一直把一些连接放在池里不归还,就会很占资源,并且是这些资源利用率降低,那么怎样才能更好的管理池子中的资源呢,commons-pool里提供了一个GenericObjectPool类,它的出现使上面的问题就迎刃而解了。同样对于GenericObjectPool类,也就有一个对应的GenericKeyedObjectPool类。
下面还是例子说话
一个Connection类,可以想象成一个远程连接比如数据库连接等。其中包括创建连接,关闭连接,和一个print方法。
- package com.googlecode.garbagecan.commons.pool.sample3;
-
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
-
- public class MyConnection {
-
- private static Logger logger = LoggerFactory.getLogger(MyConnection.class);
-
- private String name;
- private boolean connected;
-
- public MyConnection(String name) {
- this.name = name;
- }
-
- public void connect() {
- this.connected = true;
- logger.info(name + ": " + connected);
- }
-
- public void close() {
- this.connected = false;
- logger.info(name + ": " + connected);
- }
-
- public boolean isConnected() {
- return this.connected;
- }
-
- public String getName() {
- return this.name;
- }
-
- public void print() {
- logger.info(this.name);
- }
- }
一个PoolableObjectFactory接口的实现类,提供makeObject, activateObject, passivateObject, validateObject, destroyObject方法。
- package com.googlecode.garbagecan.commons.pool.sample3;
-
- import org.apache.commons.pool.PoolableObjectFactory;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
-
- public class MyConnectionPoolableObjectFactory implements PoolableObjectFactory {
-
- private static Logger logger = LoggerFactory.getLogger(MyConnectionPoolableObjectFactory.class);
-
- private static int count = 0;
-
- public Object makeObject() throws Exception {
- MyConnection myConn = new MyConnection(generateName());
- logger.info(myConn.getName());
- myConn.connect();
- return myConn;
- }
-
- public void activateObject(Object obj) throws Exception {
- MyConnection myConn = (MyConnection)obj;
- logger.info(myConn.getName());
- }
-
- public void passivateObject(Object obj) throws Exception {
- MyConnection myConn = (MyConnection)obj;
- logger.info(myConn.getName());
- }
-
- public boolean validateObject(Object obj) {
- MyConnection myConn = (MyConnection)obj;
- logger.info(myConn.getName());
- return myConn.isConnected();
- }
-
- public void destroyObject(Object obj) throws Exception {
- MyConnection myConn = (MyConnection)obj;
- logger.info(myConn.getName());
- myConn.close();
- }
-
- private synchronized String generateName() {
- return "conn_" + (++count);
- }
- }
一个测试类
其中包含了三个方法,分别测试了三种情况;
- 类中包含了一个实现了Runnable接口的内部类,目的是为了启动几个线程来模拟的对连接类的使用,并且为了尽可能的真实,在run方法里sleep了10秒中;
- 首先运行测试方法test1()可以看到,在循环10个线程申请Connection类时,前面5个可以很好的获取,但是后面5个线程就不能获取连接,并且抛出了异常,这是由于“config.maxActive = 5;”和“config.maxWait = 5 * 1000;”在起作用,由于配置了最大活动连接是5个,并且后续申请没有有效连接的等待时间是5秒,所以test1方法中后面五个线程在等了5秒后全部抛出异常,表明不能申请连接了。
- 下面运行test2()方法,在test2中把“config.maxWait = 20 * 1000;”改成了20秒,而我们程序中每个线程使用连接会用去10秒,所以后面五个线程在等待了10秒后就全部获取连接了,所以程序最后会运行成功。
- 再看test3()方法,其中把maxIdle和minIdle都改为0,就是在连接不用时立即真正归还连接,对于数据库连接来说就是关闭物理连接,而maxWait改为-1,就是如果没有申请到连接就永远等待,运行test3()方法,观察日志,可以看出程序在用户连接对象以后,会调用MyConnectionPoolableObjectFactory.destroyObject()和MyConnection.close()方法来销毁对象。所以如果是使用这样的配置,就相当于每次都是物理连接,用完后就关闭连接。当然这里是一个极端的例子,真实情况下不会把maxIdle和minIdle都设为0的。
其实对于GenericObjectPool.Config类和GenericKeyedObjectPool.Config类还是有很多配置参数的,这里只是列出的最简单的几个常用的,具体可以参考官方文档。