我们先来写测试连接池的测试程序,我们模拟30个线程并发,同时去访问我们的redis连接池
Test.java
package com.zzq.core.test;
import java.util.concurrent.CountDownLatch;
import redis.clients.jedis.Jedis;
import com.zzq.core.pool.ConnectionPool;
import com.zzq.core.pool.ConnectionPoolImpl;
public class Test {
private static int threadCount = 30;
private final static CountDownLatch COUNT_DOWN_LATCH = new CountDownLatch(threadCount); //为保证30个线程同时并发运行
public static void main(String[] args) {
final ConnectionPool pool = new ConnectionPoolImpl();
//连接池最大连接数和获取连接的超时时间
pool.init(10, 2000L);
for(int i = 0 ; i < threadCount ; i++){//循环开30个线程
new Thread(new Runnable() {
public void run() {
int i = 0;
while (i < 10) {//每个线程里循环十次获取连接
i++;
Jedis jedis = null;
try {
COUNT_DOWN_LATCH.countDown();//每次减一
COUNT_DOWN_LATCH.await(); //此处等待状态,为了让30个线程同时进行
jedis = pool.borrowResource();
jedis.get("a");
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
pool.release(jedis); //释放连接
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}).start();
}
}
}
然后是核心代码 (需要jedis的jar包)
package com.zzq.core.pool;
import redis.clients.jedis.Jedis;
public interface ConnectionPool {
/**
* zhouzhongqing
* 2017年9月16日12:28:56
* 初始化线程池
* max 最大连接数
* maxWait 最大等待时间
* */
void init(int maxActive ,long maxWait);
/**
* zhouzhongqing
* 2017年9月16日12:29:11
* 获取连接
* */
Jedis borrowResource()throws Exception;
/****
* zhouzhongqing
* 2017-9-16 12:30:38
* 释放连接
* **/
void release(Jedis jedis)throws Exception;
/****
* zhouzhongqing
* 2017-9-16 12:30:59
* 关闭
* **/
void close();
}
实现类:
package com.zzq.core.pool;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import redis.clients.jedis.Jedis;
public class ConnectionPoolImpl implements ConnectionPool {
//是否关闭
AtomicBoolean isClosed = new AtomicBoolean(false);
//队列实现连接 对象存储
LinkedBlockingQueue idle; //空闲队列
LinkedBlockingQueue busy; //繁忙队列
//大小控制连接数量
AtomicInteger activeSize = new AtomicInteger(0);
//记录连接被创建的次数
AtomicInteger createCounter = new AtomicInteger(0);
int maxActive;
long maxWait;
public void init(int maxActive, long maxWait) {
this.maxActive = maxActive;
this.maxWait = maxWait;
idle = new LinkedBlockingQueue();
busy = new LinkedBlockingQueue();
}
public Jedis borrowResource() throws Exception {
Jedis jedis = null;
long now = System.currentTimeMillis();//获取连接的开始时间
while(null == jedis){
//从空闲队列中获取一个
jedis = idle.poll();
if(null != jedis){
//如果空闲队列里有连接,直接是被复用,再将此连接移动到busy (繁忙)队列中
busy.offer(jedis);
System.out.println("从空闲队列里拿到连接");
return jedis;
}
//如果空闲队列里没有连接,就判断是否超出连接池大小,不超出就创建一个
if(activeSize.get() < maxActive){//多线程并发
//先加再判断
if(activeSize.incrementAndGet() <= maxActive){
//创建jedis连接
jedis = new Jedis("127.0.0.1", 6379);
System.out.println("连接被创建的次数:" +createCounter.incrementAndGet());
//存入busy队列
busy.offer(jedis);
return jedis;
}else{
//加完后超出大小再减回来
activeSize.decrementAndGet();
}
}
//如果前面2个都不能拿到连接,那就在我们设置的最大等待超时时间内,等待别人释放连接
try {
//等待别人释放得到连接,同时也有最长的等待时间限制
jedis = idle.poll(maxWait - (System.currentTimeMillis() - now), TimeUnit.MILLISECONDS);
} catch (Exception e) {
throw new Exception("等待异常 ... ");
}
if(null == jedis ) {
//判断是否超时
if( (System.currentTimeMillis() - now) >= maxWait ){
throw new Exception("timeout ... ");
}else{
continue;
}
}else{
//存入busy队列
busy.offer(jedis);
}
}
return jedis;
}
public void release(Jedis jedis) throws Exception {
if(null == jedis ) {
System.out.println("释放 的jedis为空");
return;
}
if(busy.remove(jedis)){
idle.offer(jedis);
}else{
//如果释放不成功,则减去一个连接,在创建的时候可以自动补充
activeSize.decrementAndGet();
throw new Exception("释放jedis异常");
}
}
public void close() {
if(isClosed.compareAndSet(false, true)){
LinkedBlockingQueue pool = idle;
while(pool.isEmpty()){
Jedis jedis = pool.poll();
jedis.close();
if(pool == idle && pool.isEmpty() ){
pool = busy;
}
}
}
}
}
运行代码时先运行redis,我这边为了方便用了个Windows的
运行结果:
连接最大被创建10,其他的连接是空闲队列得到的或者是在设置的最长等待时间从空闲队列得到到,已经达到了我们想要的效果了。
附上源码和安装包(Windows redis和redis-desktop-manager) http://download.csdn.net/download/baidu_19473529/9981750
推荐看看手写精简版的java线程池,这两篇都是池话技术,可能更容易理解线程池和连接池的区别和相近之处等等。代码或者我理解上有问题的地方,还请不吝指正。