在我们日常对数据库操作时存在一个问题,要为每次数据操作请求建立一个数据库连接。而每次建立连接都需要花费很多开销,如加载驱动类、注册驱动、获取连接,这样如果在短时间内连接多次,就
会耗费多余的时间(加载驱动+注册驱动)*n次; 那么就有了数据库连接池这种解决方案: 这样就节省了很多时间。而关闭数据连接与上面是一样的,就不再画了。下面是用java实现数据库连接池并分析两种方式的时间消耗:首先是DBconnectPool.java,相当于把业务都抽象出来
package Pool;import java.util.*;import java.sql.*;/** * 项目名: CSDN 包名: Pool 文件名: DBconnectPool.java 创建时间: 2019年4月14日 * * @author: xiatom 描述:建立数据连接池 * * **/public class DBconnectPool { // 可用数据库连接,也就是数据连接池,因为要线程安全所以使用Vector private VectorfreeConnection = new Vector<>(); private int maxConn;// 最大连接数 private int normalConn;// 保持连接数 private String pass; private String user; private String url; private int numActive = 0;// 当前活动连接数 private static int num = 0;// 当前空闲连接数 public DBconnectPool(String url, String user, String pass, int maxConn, int normalConn) { this.user = user; this.url = url; this.pass = pass; this.maxConn = maxConn; this.normalConn = normalConn; for (int i = 0; i < normalConn; i++) { Connection con = newConnection(); if (con != null) { freeConnection.addElement(con); num++; } } } //新建连接,也就是第二张图的小格子。省去每次加载注册驱动时间 private Connection newConnection() { Connection con = null; try { if (user == null) con = DriverManager.getConnection(url); else con = DriverManager.getConnection(url, user, pass); System.out.println("新建一个数据库链接"); } catch (SQLException e) { System.out.println("新建数据库链接失败,错误:" + e); return null; } return con; } //获取当前空闲连接 public int getNum() { return num; } //获取当前使用连接 public int getNumActive() { return numActive; } public synchronized Connection getConnection() { Connection con = null; System.out.println(Thread.currentThread().getName() + "开始获取数据库链接"); if (freeConnection.size() > 0) { num--; con = freeConnection.elementAt(0); freeConnection.removeElementAt(0); // 未考虑在数据池中已关闭的连接,若考虑需要自己加 } else if (maxConn == 0 || normalConn < maxConn) { con = newConnection(); } if (con != null) System.out.println(Thread.currentThread().getName() + "获取到一个数据库链接"); else System.out.println("得到空的数据库连接"); numActive++; return con; } public synchronized void freeConnection(Connection con) { freeConnection.addElement(con); num++; numActive--; notifyAll(); } //关闭所有的连接 public synchronized void release() { for (Connection con : freeConnection) { try { con.close(); num--; System.out.println("关闭一个数据库链接"); } catch (SQLException e) { System.out.println("释放数据链接池失败"); } } if (num == 0) System.out.println("释放所有链接"); freeConnection.removeAllElements(); numActive = 0; }}
下面是Pool.java,实现数据库连接池:
package Pool;import java.sql.Connection;import java.sql.Driver;import java.sql.DriverManager;import java.sql.SQLException;/** * 项目名: CSDN 包名: Pool 文件名: Pool.java 创建时间: 2019年4月15日 * * @author: xiatom 描述: * * **/public class Pool { private static Pool instance = null; private int maxConn = 100; private int normalConn = 10; private String password = "1023"; private String user = "root"; private String url = "jdbc:mysql://localhost:3306/test"; private String driverName = "com.mysql.jdbc.Driver"; DBconnectPool dbpool = null; Driver dbDriver = null; private Pool() { loadDriver(driverName); createPool(); } private void createPool() { dbpool = new DBconnectPool(url, user, password, maxConn, normalConn); } private void loadDriver(String driver) { try { dbDriver = (Driver) Class.forName(driver).newInstance(); // DriverManager.registerDriver(dbDriver); System.out.println("注册驱动类" + driver + "成功"); } catch (Exception e) { System.out.println("无法注册驱动类" + driver + " 错误:" + e); } } public void freeCon(Connection con) { if (con != null) { dbpool.freeConnection(con); System.out.println("释放成功"); } else System.out.println("传递的是一个空连接"); } public static Pool getInstance() { if (instance == null) instance = new Pool(); return instance; } public Connection getCon() { return dbpool.getConnection(); } public int getNum() { return dbpool.getNum(); } public int getNumActive() { return dbpool.getNumActive(); } public synchronized void release() { dbpool.release(); try { DriverManager.registerDriver(dbDriver); System.out.println("撤销驱动成功"); } catch (SQLException e) { System.out.println("撤销驱动失败"); e.printStackTrace(); } }}
上面synchronized关键字是为了保证线程安全加的锁
如果没有锁:举个例子DBconnectPool类的getConnection方法num--; con = freeConnection.elementAt(0);如果不加锁,则有两个线程获取连接时,都执行到上面那一步,那么他们获取的是同一个连接,因为这时freeConnection并没有发生改变,首元素是一样的。然后他们在都执行下面这句 freeConnection.removeElementAt(0);就会导致两个线程用的同一个连接,然后连接池丢失了一个链接
下面是依照此方法,建立10个数据连接耗费的时间
package Pool;import java.sql.Connection;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;/*** 项目名: CSDN* 包名: Pool* 文件名: MutiThreadTest.java* 创建时间: 2019年4月15日* * @author: xiatom* 描述: * ***/public class ThreadTest implements Runnable{ static Pool pool = null; public void test() { } @Override public void run() { Connection con = pool.getCon(); System.out.println("剩余"+pool.getNum()+"个可用连接"); } public static void main(String[] args) { pool = Pool.getInstance(); ThreadTest tt = new ThreadTest(); Thread t[] = new Thread[10]; long start = System.currentTimeMillis(); for(int i=0;i<10;i++) { new Thread(tt,"Thread-"+(i+1)).start(); } while(pool.getNum()!=0) { } try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("使用时间:"+(System.currentTimeMillis()-start)); }}由于我为了等所有线程结束所以多用了1毫秒去等待,所以实际使用时间为3毫秒左右。
下面是常规方法,建立数据连接:
import java.sql.*;public class Connect { private Connection connection; Connect() { try { Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql://localhost:3306/test"; this.connection = DriverManager.getConnection(url,"root","1023"); } catch (ClassNotFoundException | SQLException e) { e.printStackTrace(); } } public Connection getCon() { return this.connection; }}
测试类
import java.sql.*;public class Main { public static void main(String[] args) throws SQLException { long start = System.currentTimeMillis(); for(int i=0;i<10;i++) { new Connect().getCon(); } long end = System.currentTimeMillis()-start; System.out.println("消耗时间:"+end); }}差距就很明显了。