1、数据库连接池是什么?
数据库连接是一种昂贵的资源,创建数据库连接是一个耗时的操作,在多线程并发条件下尤为突出,对数据库连接的高效管理能影响到程序的性能指标,数据库连接池正是针对这个问题提出来的。数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个新连接,利用数据库连接池将明显提高对数据库操作的性能;
2、数据库连接池有哪些技术方案?
(1)C3P0
(2)DBCP
(3)Proxool
(4)Tomcat Jdbc Pool
(5)BoneCP
(6)Druid
(7)HiKariCP
数据库连接池属于一种池化技术;
池化技术:http访问(httpclient)、redis访问(redisPool)、线程(线程池)等;
面试:(有现场需要写代码的面试)
1、手写一个队列(不能使用jdk自带的工具类)
2、手写一个栈
3、手写一个HashMap
4、手写一个生产者消费者
5、手写一个(jvm)缓存,甚至不能用jdk中现有的类
6、......
纯手写数据库连接池实现
------MyDataSourceInterface
package com.atqiyu.pool;
import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;
/**
* @author 7575
* @version 1.0
* 我亦无他,唯手熟尔!
*/
public interface MyDataSourceInterface extends DataSource {
@Override
default Connection getConnection() throws SQLException {
return null;
}
@Override
default Connection getConnection(String username, String password) throws SQLException {
return null;
}
//下面的方法可以不实现
@Override
default <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
default boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
@Override
default PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
default void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
default void setLoginTimeout(int seconds) throws SQLException {
}
@Override
default int getLoginTimeout() throws SQLException {
return 0;
}
@Override
default Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
}
------MyAbstractDataSource
package com.atqiyu.pool;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* @author 7575
* @version 1.0
* 我亦无他,唯手熟尔!
*/
public abstract class MyAbstractDataSource implements MyDataSourceInterface {
private String url;
private String driver;
private String user;
private String password;
//最大的正在使用的连接数
private int poolMaxActiveConnections = 10;
//最大的空闲连接数
private int poolMaxIdleConnections = 5;
//从连接池中获取一个连接最大等待多少毫秒
private int poolTimeToWait = 30000;
public int getPoolMaxActiveConnections() {
return poolMaxActiveConnections;
}
public void setPoolMaxActiveConnections(int poolMaxActiveConnections) {
this.poolMaxActiveConnections = poolMaxActiveConnections;
}
public int getPoolMaxIdleConnections() {
return poolMaxIdleConnections;
}
public void setPoolMaxIdleConnections(int poolMaxIdleConnections) {
this.poolMaxIdleConnections = poolMaxIdleConnections;
}
public int getPoolTimeToWait() {
return poolTimeToWait;
}
public void setPoolTimeToWait(int poolTimeToWait) {
this.poolTimeToWait = poolTimeToWait;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getDriver() {
return driver;
}
public void setDriver(String driver) {
this.driver = driver;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public Connection getConnection() throws SQLException {
return getConnection(user, password);
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return doGetConnection(username, password);
}
/**
* 获取数据库连接
*
* @param username
* @param password
* @return
*/
private Connection doGetConnection(String username, String password) throws SQLException {
Connection connection = DriverManager.getConnection(url, username, password);
return connection;
}
}
------MyDataSource
package com.atqiyu.pool;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* @author 7575
* @version 1.0
* 我亦无他,唯手熟尔!
*
* 数据源的连接池
*/
public class MyDataSource extends MyAbstractDataSource {
//空闲连接池
private final List<ConnectionProxy> idleConnections = new ArrayList<ConnectionProxy>();
//激活的连接池
private final List<ConnectionProxy> activeConnections = new ArrayList<ConnectionProxy>();
//监视器对象 用于作一些同步操作相关的一些使用
private final Object monitor = new Object();
//监视器对象 用于作一些同步操作相关的一些使用
private final Object watch = new Object();
/**
* 覆盖父类的方法,返回的是一个代理连接
* @return
* @throws SQLException
*/
@Override
public Connection getConnection() throws SQLException {
ConnectionProxy connectionProxy = getConnectionProxy(super.getUser(),super.getPassword());
return connectionProxy.getProxyConnection();
}
/**
* 获取连接
* @param username
* @param password
* @return
*/
public ConnectionProxy getConnectionProxy(String username,String password) throws SQLException {
boolean wait = false;
ConnectionProxy connectionProxy = null;
//刚开始是没有连接的
while (connectionProxy == null) {
//做一个同步线程
synchronized (monitor) {
//如果空闲连接不为空,那么就可以直接获取到连接
if (!idleConnections.isEmpty()) {
connectionProxy = idleConnections.remove(0);
} else {
//没有空闲连接可以使用,那么我们需要获取新的连接(也就是需要创建一个连接)
if (activeConnections.size() < super.getPoolMaxActiveConnections()) {
//如果当前激活的连接数 小于 我们允许的最大连接数,那么此时可以创建一个新的连接,否则还不能创建
connectionProxy = new ConnectionProxy(super.getConnection(),this);
}
//否则是不能创建新连接的,需要等待,等 private int poolTimeToWait = 30000; 毫秒
}
}
if (!wait) {
wait = true;
}
if (connectionProxy == null) {
try {
//连接对象是空,那么需要等待;
monitor.wait(super.getPoolTimeToWait());
} catch (InterruptedException e) {
e.printStackTrace();
//万一等待被循环打断,退出一下循环
break;
}
}
}
if (connectionProxy != null) {
//连接对象不为空,说明已经拿到连接了
activeConnections.add(connectionProxy);
}
//返回连接对象
return connectionProxy;
}
/**
* 关闭连接,但是不是把连接关闭,而是把连接池归还到连接池
* @param connectionProxy
*/
public void closeConnection(ConnectionProxy connectionProxy) {
synchronized (monitor) {
//关闭连接,那么就是把激活状态的连接,变成空闲连接
activeConnections.remove(connectionProxy);
if (idleConnections.size() < super.getPoolMaxIdleConnections()) {
idleConnections.add(connectionProxy);
}
//通知一下,唤醒上面那个等待获取连接的线程
monitor.notifyAll();
}
}
}
------ConnectionProxy
package com.atqiyu.pool;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
/**
* @author 7575
* @version 1.0
* 我亦无他,唯手熟尔!
*
* 采用动态代理实现对数据库连接的代理
*/
public class ConnectionProxy implements InvocationHandler {
//真实的连接
private Connection realConnection;
//代理连接
private Connection proxyConnection;
//持有数据源对象
private MyDataSource myDataSource;
public Connection getRealConnection() {
return realConnection;
}
public void setRealConnection(Connection realConnection) {
this.realConnection = realConnection;
}
public Connection getProxyConnection() {
return proxyConnection;
}
public void setProxyConnection(Connection proxyConnection) {
this.proxyConnection = proxyConnection;
}
public MyDataSource getMyDataSource() {
return myDataSource;
}
public void setMyDataSource(MyDataSource myDataSource) {
this.myDataSource = myDataSource;
}
/**
* 构造方法
*
* @param realConnection
* @param myDataSource
*/
public ConnectionProxy(Connection realConnection, MyDataSource myDataSource) {
//初始化真实连接
this.realConnection = realConnection;
//初始化数据源
this.myDataSource = myDataSource;
//初始化代理连接
this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(),
new Class<?>[] {Connection.class},
this);
}
/**
* 当调用Connection对象里面的方法时,首先会被该invoke方法拦截
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//获取到当前调用了Connection对象的什么方法
String methodName = method.getName();
if (methodName.equalsIgnoreCase("close")) {
//TODO 把连接归还到连接池
myDataSource.closeConnection(this);
return null;
} else {
return method.invoke(realConnection,args);
}
}
}