您的位置:首页 > 编程语言 > Java开发

用JAVA 实现一个简版数据库连接池

2018-02-06 11:51 716 查看
在JAVA工程师的面试当中,数据库连接池的实现,肯定也是多线程等待超时模式的一个重要实现案例,通过网上的查阅,对数据连接池有了一定的了解,然后就自己写了一个简陋版,以供理解。
下面贴出代码:
1.创建数据库连接的驱动类,在真实环境中有数据库厂商提供驱动包。如mysql:mysql-connectorxxxx.jar
ConnectDriver(通过动态代理,对commit方法进行装饰):

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.util.concurrent.TimeUnit;

public class ConnectionDriver {

static class ConnectionHandler implements InvocationHandler{
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

if(method.getName().equals("commit")){
System.out.println("I am commit!!!");
TimeUnit.MILLISECONDS.sleep(100);
}
return null;
}
}

public static final Connection createConnection(){

return (Connection) Proxy.newProxyInstance(ConnectionDriver.class.getClassLoader(),new Class<?>[]{ Connection.class},new ConnectionHandler());

}

}2.数据连接池类(重要)。 我用过的有Pop3数据库连接池。
import java.sql.Connection;
import java.util.LinkedList;

public class MyDatabaseConnectPool {

private static final int DEFAULT_SIZE=20;

private static final int MINSIZE=1;

private static final int MAXSIZE=100;

//数据链接队列
private LinkedList<Connection> connectionList=new LinkedList<>();

//无参数构造
public MyDatabaseConnectPool() {

for(int i=0;i<DEFAULT_SIZE;++i){
connectionList.add(ConnectionDriver.createConnection());
}
}

//初始化连接池
public MyDatabaseConnectPool(int initialSize) {

if(initialSize>0){

initialSize=initialSize<MINSIZE? MINSIZE:initialSize;
initialSize=initialSize>MAXSIZE ? MAXSIZE:initialSize;

for(int i=0;i<initialSize;++i){
connectionList.add(ConnectionDriver.createConnection());
}

}else{
throw new Error("initialSize must >0");
}
}

public void releaseConnection(Connection connection){

if(connection!=null){

synchronized (connectionList){
//将空闲链接放入队列
connectionList.addLast(connection);

//通知其他等待的线程 有可用的链
connectionList.notifyAll();
}
}

}

//获取链接函数
public Connection getConnection(long mills) throws InterruptedException {

//获取队列的锁
synchronized (connectionList){

if(mills<=0){

while (connectionList.isEmpty()){

try {
connectionList.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

return connectionList.removeFirst();

}else{

//进行超时等待获取链接

long futrer_time=System.currentTimeMillis()+mills;

long remain_time=mills;

while (remain_time>0 && connectionList.isEmpty()){

connectionList.wait(remain_time);

//先等待后再获取剩余时间
remain_time=futrer_time-System.currentTimeMillis();
}

Connection connection=null;

//如果此时队列不为空,获取链接
if(!connectionList.isEmpty()){
connection=connectionList.removeFirst();
}

return connection;
}

}

}

}

分析:此处的队列使用LinkedList ,是为了使用链表增删节点性能较好。还可以使用下面的方法:
 connectionList.addLast(connection);   
 connectionList.removeFirst();
3.测试类:
import java.sql.Connection;
import java.sql.SQLException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

public class Test {

static MyDatabaseConnectPool pool = null;

//设定两个闭关锁 一个用于开启线程活动 一个用于监控所有线程的工作完成状态
static CountDownLatch start = new CountDownLatch(1);

static CountDownLatch end;

public static void main(String[] args) {

System.out.println("我们开始运行!!!!");

pool=new MyDatabaseConnectPool(-5);
int thread_count=10;
int count=20;

end=new CountDownLatch(thread_count);
AtomicInteger success_count=new AtomicInteger(0);
AtomicInteger faile_count=new AtomicInteger(0);

//开启多个线程
for(int i=0;i<thread_count;++i){
new MyConnectUser(count,success_count,faile_count).start();
}

//发出 可以获取链接 的命令
start.countDown();

try {
//此处锁住 等待所有线程完成工作
end.await();
} catch (InterruptedException e) {
e.printStackTrace();
}

//打印各个指标

System.out.println("The total try times :"+thread_count*count);
System.out.println("The total success times: "+success_count);
System.out.println("The totla faile times :"+faile_count);

}

private static class MyConnectUser extends Thread {

//线程内获取次数
int count;

//分别用来统计 成功/失败 获取次数
AtomicInteger success_count;
AtomicInteger faile_cout;

public MyConnectUser(int count, AtomicInteger success_count, AtomicInteger faile_cout) {

this.count = count;
this.success_count = success_count;
this.faile_cout = faile_cout;

}

@Override
public void run() {

try {
start.await();
} catch (InterruptedException e) {
e.printStackTrace();
}

while (count>0){

try {
//尝试获取链接
Connection connection= pool.getConnection(50);

if(connection!=null) {

try {
//模拟真实使用链接
connection.createStatement();
connection.commit();
} catch (SQLException e) {
e.printStackTrace();
}finally {
//成功次数加1
success_count.incrementAndGet();
//释放链接
pool.releaseConnection(connection);
}

}else{
//失败次数加1
faile_cout.incrementAndGet();
}

} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//线程内尝试获取次数减一
count--;

}
}
//告诉end 我这个线程的工作完成
end.countDown();
}
}

}总结:这只是我对数据库连接池的理解和简单实现,真是的数据库连接池肯定是要复杂的多。如果有出错的地方请各位在评论处指出。

                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息