commons-pool 对象池
commons-pool 中实现了多个对象池类,对象池类可以用于数据库连接池,tcp客户端池。
maven依赖:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.4.1</version>
</dependency>
3种对象池
在org.apache.commons.pool2.impl中预设了三个可以直接使用的对象池:GenericObjectPool、GenericKeyedObjectPool和SoftReferenceObjectPool。
通常使用GenericObjectPool来创建对象池,如果是对象池是Keyed的,那么可以使用GenericKeyedObjectPool来创建对象池。这两个类都提供了丰富的配置选项。这两个对象池的特点是可以设置对象池中的对象特征,包括LIFO(后进先出)方式、最大空闲数、最小空闲数、是否有效性检查等等。两者的区别如前面所述,后者支持Keyed。
而SoftReferenceObjectPool对象池,它利用一个java.util.ArrayList对象来保存对象池里的对象。不过它并不在对象池里直接保存对象本身,而是保存它们的“软引用”
(Soft Reference)。这种对象池的特色是:可以保存任意多个对象,不会有容量已满的情况发生;在对象池已空的时候,调用它的borrowObject方法,会自动返回新创建的实例;可以在初始化同时,在池内预先创建一定量的对象;当内存不足的时候,池中的对象可以被Java虚拟机回收。
实例
MyBean测试对象
import java.sql.Timestamp;
public class MyBean {
public static final String[] names = { "Littlehow", "Jim Green", "Black Tom", "White Cat", "Yellow Dog",
"Color Wolf" };
/**
* bean被初始化的时间
*/
private long instanceTime = System.currentTimeMillis();
/**
* 名称
*/
private String name;
/**
* 对象是否存活
*/
private boolean live = true;
/**
* 实例化对象
*/
public MyBean() {
this.name = names[(int) (this.instanceTime % names.length)];
}
/**
* name由调用方指定
*
* @param name
*/
public MyBean(String name) {
this.name = name;
}
/**
* 销毁方法
*/
public void beKilled() {
System.out.print("我[" + this.name + "]居然被销毁了,我不甘心啊,");
System.out.println("就活了[" + (System.currentTimeMillis() - this.instanceTime) + "]毫秒!");
}
public String toString() {
return "我[" + this.name + "]出生在:" + new Timestamp(this.instanceTime);
}
/**
* 获取实例化时间
*
* @return
*/
public long getInstanceTime() {
return this.instanceTime;
}
/**
* 获取对象标志
*
* @return
*/
public String getName() {
return this.name;
}
/**
* 死亡掉
*/
public void deadBean() {
this.live = false;
}
/**
* 人不是还活着
*
* @return
*/
public boolean isLive() {
return this.live;
}
/**
* 相当于打开一个链接对象,如果是管理数据库连接的话
*/
public void start() {
System.out.println(this.name + "的生命开始了");
}
}
KeyPoolFactory工厂类
import org.apache.commons.pool2.BaseKeyedPooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
public class KeyPoolFactory {
/**
* 对象池
*/
private static GenericKeyedObjectPool<String, MyBean> pool;
/**
* 对象池的参数设置
*/
private static final GenericKeyedObjectPoolConfig config;
/**
* 对象池每个key最大实例化对象数
*/
private final static int TOTAL_PERKEY = 10;
/**
* 对象池每个key最大的闲置对象数
*/
private final static int IDLE_PERKEY = 3;
static {
config = new GenericKeyedObjectPoolConfig();
config.setMaxTotalPerKey(TOTAL_PERKEY);
config.setMaxIdlePerKey(IDLE_PERKEY);
/** 支持jmx管理扩展 */
config.setJmxEnabled(true);
config.setJmxNamePrefix("myPoolProtocol");
/** 保证获取有效的池对象 */
config.setTestOnBorrow(true);
config.setTestOnReturn(true);
}
/**
* 从对象池中获取对象
*
* @param key
* @return
* @throws Exception
*/
public static MyBean getBean(String key) throws Exception {
if (pool == null) {
init();
}
return pool.borrowObject(key);
}
/**
* 归还对象
*
* @param key
* @param bean
*/
public static void returnBean(String key, MyBean bean) {
if (pool == null) {
init();
}
pool.returnObject(key, bean);
}
/**
* 关闭对象池
*/
public synchronized static void close() {
if (pool != null && !pool.isClosed()) {
pool.close();
pool = null;
}
}
/**
* 初始化对象池
*/
private synchronized static void init() {
if (pool != null)
return;
pool = new GenericKeyedObjectPool<String, MyBean>(new MyBeanPooledFactory(), config);
}
/**
* 对象工厂
*/
static class MyBeanPooledFactory extends BaseKeyedPooledObjectFactory<String, MyBean> {
/**
* 创建对象
*
* @param key
* @return
* @throws Exception
*/
public MyBean create(String key) throws Exception {
MyBean myBean = new MyBean();
myBean.start();
System.out.println(myBean);
return myBean;
}
public PooledObject<MyBean> wrap(MyBean value) {
return new DefaultPooledObject<MyBean>(value);
}
/**
* 验证对象是否有效
*
* @param key
* @param p
* @return
*/
public boolean validateObject(String key, PooledObject<MyBean> p) {
MyBean bean = p.getObject();
if (!bean.isLive()) {
System.out.println(bean.getName() + "已经死了,无法唤醒他了!");
return false;
}
return true;
}
/**
* 销毁
*
* @param key
* @param p
* @throws Exception
*/
public void destroyObject(String key, PooledObject<MyBean> p) throws Exception {
/** 杀死他 */
p.getObject().beKilled();
}
public void activateObject(String key, PooledObject<MyBean> p) throws Exception {
super.activateObject(key, p);
}
public void passivateObject(String key, PooledObject<MyBean> p) throws Exception {
super.passivateObject(key, p);
}
}
}
GetBean测试类
import static org.junit.Assert.assertTrue;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
public class GetBean {
static String[] keys = {"2016052700", "2016052800", "2016052900"};
/** 随机key */
Random r = new Random();
/**
* 获取key值
* @return
*/
String getKey () {
return keys[r.nextInt(keys.length)];
}
/**
* 获取对象
*/
@Test
public void getBean () {
String key = getKey();
/** 保证key值长度为10 */
assertTrue( "key的长度必须为10", key != null && key.length() == 10 );
System.out.println(key);
try {
/**
* 同一个key最多可以获取10个对象
* 可以看出,当获取10个对象时,对象池就再也不能给出对象了
*/
for (int i = 0; i < 10; i ++) {
TimeUnit.SECONDS.sleep(1);//睡一秒
KeyPoolFactory.getBean(key);
}
/** 可以看出拿对象时阻塞的 */
System.out.println("前方是否阻塞到我了...");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取对象再还回去
*/
@Test
public void getAndReturnBean () {
String key = getKey();
/** 保证key值长度为10 */
assertTrue( "key的长度必须为10", key != null && key.length() == 10 );
try {
/**
* 同一个key最多可以获取10个对象
* 可以看出,当获取10个对象时,对象池就再也不能给出对象了
*/
for (int i = 0; i < 20; i ++) {
TimeUnit.SECONDS.sleep(1);//睡一秒
MyBean my = KeyPoolFactory.getBean(key);
System.out.println(my.getName());
/** 让对象死掉 */
// my.deadBean();
/** 归还对象
* 因为执行了my.deadBean(),这时候在归还对象时,他发现对象已经
* 不合法了,这时候工厂就会重新拿对象
* 如果屏蔽掉my.deadBean()这样的方法,那么获取到的对象将是第一次获取到的对象
*/
KeyPoolFactory.returnBean(key, my);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 多个key同时去获取bean
*/
@Test
public void getBeans () {
String key1 = getKey();
String key2 = key1;
while (key1.equals(key2)) {
key2 = getKey();
}
assertTrue( "key的长度必须为10,而获取到的key1="+key1, key1 != null && key1.length() == 10 );
assertTrue( "key的长度必须为10,而获取到的key2="+key2, key2 != null && key2.length() == 10 );
try {
/**
* 同一个key最多可以获取10个对象
* 20个对象用2个key就可以全部输出
* 这些都是不归还的
*/
for (int i = 0; i < 20; i ++) {
TimeUnit.SECONDS.sleep(1);//睡一秒
if (i % 2 == 0){
KeyPoolFactory.getBean(key1);
} else {
KeyPoolFactory.getBean(key2);
}
}
/**
* 可以看出这句输出了
*/
System.out.println("前方是否阻塞到我了...");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 一个线程借对象,一个线程还对象
* 如果获取和归还的时间差特别大,会导致某些时候线程阻塞,因为对象已经拿完了
* 就好像借书一样,如果书被借完了,那么再想借就得需要等借书人还书了。
*/
volatile boolean dead = false;//死亡对象标志
@Test
public void getAndReturnByThread () {
/** 对象缓存 */
final ConcurrentHashMap<String, LinkedBlockingDeque<MyBean>> beans = new ConcurrentHashMap<String, LinkedBlockingDeque<MyBean>>();
/**
* 借对象,每秒借一个
*/
new Thread("borrow") {
public void run(){
while (true) {
String key = getKey();
LinkedBlockingDeque<MyBean> link = beans.get(key);
if (link == null) {
/** 最多存放10个对象 */
link = new LinkedBlockingDeque<MyBean>(10);
beans.put(key, link);
}
try {
MyBean bean = KeyPoolFactory.getBean(key);
link.push(bean);
System.out.println(Thread.currentThread().getName() + "操作:key=" + key + ",bean=" + bean);
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}.start();
/**
* 还对象,每3秒还一个
*/
new Thread("return") {
public void run () {
while (true) {
String key = getKey();
LinkedBlockingDeque<MyBean> link = beans.get(key);
if (link == null || link.size() == 0) continue;
/** 弹出元素 */
MyBean bean = link.pop();
if (dead) {
bean.deadBean();
dead = false;
}
System.out.println(Thread.currentThread().getName() + "操作:key="+key + ",bean="+bean);
KeyPoolFactory.returnBean(key, bean);
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
/**
* 每5秒死亡一对象
*/
new Thread("dead") {
public void run () {
while (true) {
try {
TimeUnit.SECONDS.sleep(5);
dead = true;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
try {
TimeUnit.MINUTES.sleep(5);//5分钟后结束程序
Runtime.getRuntime().exit(0);
} catch (Exception e) {
}
}
}