在我们日常对数据库操作时存在一个问题,要为每次数据操作请求建立一个数据库连接。而每次建立连接都需要花费很多开销,如加载驱动类、注册驱动、获取连接,这样如果在短时间内连接多次,就
会耗费多余的时间(加载驱动+注册驱动)*n次;
那么就有了数据库连接池这种解决方案:
这样就节省了很多时间。而关闭数据连接与上面是一样的,就不再画了。下面是用java实现数据库连接池并分析两种方式的时间消耗:
首先是DBconnectPool.java,相当于把业务都抽象出来
package Pool;
import java.util.*;
import java.sql.*;/*** 项目名: CSDN 包名: Pool 文件名: DBconnectPool.java 创建时间: 2019年4月14日* * @author: xiatom 描述:建立数据连接池* ***/
public class DBconnectPool {// 可用数据库连接,也就是数据连接池,因为要线程安全所以使用Vectorprivate Vector<Connection> freeConnection = 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);elsecon = 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() + "获取到一个数据库链接");elseSystem.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("释放成功");} elseSystem.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() {}@Overridepublic 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);}
}
差距就很明显了。