用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();
}
}
}总结:这只是我对数据库连接池的理解和简单实现,真是的数据库连接池肯定是要复杂的多。如果有出错的地方请各位在评论处指出。
下面贴出代码:
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();
}
}
}总结:这只是我对数据库连接池的理解和简单实现,真是的数据库连接池肯定是要复杂的多。如果有出错的地方请各位在评论处指出。
相关文章推荐
- 今天的问题:一个简单的例子,请帮我解开“接口实现Java‘隐藏实现细目’”的迷惑。
- Java 的JDBC 数据库连接池实现方法
- 数据库连接池Java实现小结
- Java 的JDBC 数据库连接池实现方法
- 在Java applet中如何实现一个模式对话框?
- 使用JAVA中的动态代理实现数据库连接池
- 数据库连接池java实现小结
- 数据库连接池java实现小结
- 一个将数据文件转换成excel文件打印的java实现方法的代码片断(Struts+poi)
- 用JAVA实现一个分页类
- 一个实现MD5的简洁的java类
- 使用JAVA中的动态代理实现数据库连接池
- 使用JAVA中的动态代理实现数据库连接池
- 用JAVA实现一个分页类
- 一个实现排列和组合的JavaBean
- 数据库连接池Java实现小结
- Java.NET --一个基于Java的Microsoft.NET框架的实现
- 使用JAVA中的动态代理实现数据库连接池 Z
- Java 的JDBC 数据库连接池实现方法
- 数据库连接池java实现小结