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