在我们日常对数据库操作时存在一个问题,要为每次数据操作请求建立一个数据库连接。而每次建立连接都需要花费很多开销,如加载驱动类、注册驱动、获取连接,这样如果在短时间内连接多次,就
会耗费多余的时间(加载驱动+注册驱动)*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 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);
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);
}
}
差距就很明显了。