数据库连接池,实现及分析

版权声明:如需转载注明出处 https://blog.csdn.net/u011463794/article/details/89328832

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

在这里插入图片描述
差距就很明显了。

猜你喜欢

转载自blog.csdn.net/u011463794/article/details/89328832