在写这篇文章之前,请允许我先感谢传智博客网络课堂 , 感谢李勇老师
为什么需要使用连接池 :
在实际的BS或CS架构的程序里面,大多数程序的资料还是放在各式各样的数据库里面 。 其中值得注意的是,打开数据库联接和关闭数据库联接是一个非常消耗资源和时间的工作,慢慢的有人就开始提出使用一个循环利用的连接数据库模式 。 这个就是连接池的核心概念 。其宗旨是通过编码使数据库循环利用来达到减少打开数据库联接的次数,彻底杜绝关闭宝贵的数据库的功能(至少在下面我实现的代码里面没有关闭数据库的代码) 。
连接池实现的技术 :
(1) 先进先出存放数据的数据结构(里面存放连接对象,在java语言里面是 Connection 接口)
(2) 保证连接池对象在整个项目里面是一个实例,需要使用到 singleton(单例模式,包含饿汉式,和懒汉式) 。
(3) 使用代理模式重写 connection.close() 方法,其目的是不关闭数据库联接,而是把相应的数据库连接放回连接池。
(proxy 代理模式,其中包括动态代理和静态代理)
个人认为比较混淆的部分:
我对连接池感到困惑的一点是 connection.close() ,在我调用 tomcat ->datasource 获得数据源的时候,我每每调用 connection.close()方法的时候,总是感到很是奇怪
其原因是. 连接池是一个循环使用的资源,close()这个方法不是关闭连接的方法吗? 这个问题直到我学习完设计模式,听过李勇老师讲的jdbc的时候才恍然大悟(原来这只是一个代理模式啊)
下面是我实现的数据库连接池代码
连接池类
public class PoolConnection {
private static PoolConnection poolConnection;
private LinkedList<Connection> connectionList = new LinkedList<Connection>();
//初始化连接池容量
private final static int initSize = 1;
//连接池最大容量
private final static int maxCount =2;
//当前连接池分配出去的连接数
private int currentAllocateCount = 0;
//设定当前链接池容量
private int poolSize=initSize;
//数据库URl连接
private final static String url ="jdbc:mysql:///test";
//数据库用户名
private final static String user ="root";
//数据库密码
private final static String password ="root";
// singleton pattern(懒汉单例模式)
public static synchronized PoolConnection getPoolConnection()
{
if(poolConnection==null)
{
poolConnection = new PoolConnection();
}
return poolConnection;
}
//在初始化的时候初始化一定数量的数据库连接
private PoolConnection()
{
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
throw new ExceptionInInitializerError(e);
}
for(int i=0;i<initSize;i++)
{
//添加数据库连接到list 尾部
connectionList.addLast(initConnection());
}
}
//私有方法,提供创建数据库连接的实现
private Connection initConnection()
{
Connection connection =null;
try
{ //这里面调用的是一个实现了Connection 的代理类,代理真实的Connection对象
connection=new ProxyConnection(DriverManager.getConnection(url, user, password));
}catch(SQLException ex)
{
throw new ExceptionInInitializerError(ex);
}
return connection;
}
//从连接池里面取出一个数据库连接
public synchronized Connection getConnection()
{
//如果分发出去的数量等于 容器所允许的最大值,就让他休息一会儿,这块儿功能还没有真正的实现,在并发的时候出现错误
//如果已经分配给程序使用的数据库连接数大于规定的最大连接数,就等待其他线程把相应的连接放回连接池之后再进行下一步操作
if(currentAllocateCount>=maxCount)
{
try {
wait(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
getConnection();
}
//如果已经分配给程序使用的数据库连接数等于连接池容量
if(currentAllocateCount ==poolSize)
{
//添加数据库连接到整个List尾部
connectionList.addLast(initConnection());
//容量++
poolSize ++ ;
}
//已经分配给程序使用的数据库连接数 ++
currentAllocateCount++;
//返回并且移除List第一个数据库连接(Connection)
return connectionList.removeFirst();
}
//把相应的数据库资源放回连接池
public synchronized void freeConnection(Connection connection)
{
//已经分配给程序使用的数据库连接数 --
if(connection!=null)
currentAllocateCount--;
//添加数据库连接到整个List尾部
connectionList.addLast(connection);
}
}
代理Connection类,只显示部分关键代码
public class ProxyConnection implements Connection {
private Connection connection = null;
public ProxyConnection(Connection connection) {
this.connection = connection;
}
//改变close 方法的实现,现在的行为是如果调用close
//方法实际上是把相应的接连对象从新放回到连接池里面
@Override
public void close() throws SQLException {
PoolConnection.getPoolConnection().freeConnection(this);
}
@Override
public void commit() throws SQLException {
connection.commit();
}
}
连接池测试:
public class Test {
//现在这个测试,是打印出Connection的地址,最后的结果100条打印语句,
//其中Connection的地址是相同的,证明我们已经实现了连接池的核心功能
//打印结果 com.pool.ProxyConnection@196c1b0
// com.pool.ProxyConnection@196c1b0
// com.pool.ProxyConnection@196c1b0
public static void main(String[] args) throws SQLException {
for(int i=0;i<100;i++)
{
Connection conn = PoolConnection.getPoolConnection().getConnection();
System.out.println(conn);
conn.close();
}
}
}
总结: 到现在为止,链接池的基本功能已经实现。不过在实际编码过程中很少使用自己编写的连接池(多半是使用第三方的Jar包来实现)。我书写以上代码的目的主要是理论连接池的思想,希望我写的这篇博文会对各位朋友们有所帮助。
参考文献:
(1) Head First 设计模式(中文版) http://www.verycd.com/search/folders/head+first
(2) java与模式 http://download.youkuaiyun.com/source/3392777
(3) 传智博客 jdbc视频教程 http://www.itcast.cn/itcast_static/JDBCVideo.htm