在上一篇博客中,将5个连接放到栈里,当做数据库连接池使用,加快了效率。代码如下:
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.ResourceBundle;
import java.util.Stack;
public class DataPool {
private static Stack<Connection> pool = new Stack<Connection>();
static {
try {
//读取配置文件的信息
ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
String Driver = bundle.getString("jdbc.driver");
String url = bundle.getString("jdbc.url");
String user = bundle.getString("jdbc.username");
String password = bundle.getString("jdbc.password");
Integer size = Integer.valueOf(bundle.getString("jdbc.initialSize"));
Class.forName(Driver); 另一种写法new com.mysql.jdbc.Driver();相当于driver写入内存
for (int i = 0; i < size; i++) {
Connection connection = DriverManager.getConnection(url, user, password);//创建连接
pool.push(connection);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConn() {
return pool.pop();
}
public static void close(Connection connection) {
pool.push(connection);
}
但是有一个问题,如果当多线程连接数据库时,当超过5个线程连接数据库时,那么在第6个线程包括之后就连接不到数据库了。
这里用了java的动态代理来代理数据库的连接,拦截连接的close方法,来控制数据库连接池的连接数量。以防止多线程访问时出现获取不到连接的情况,并且给代理的连接加上一个时间属性,和实时监控的线程。初始化5个连接放到栈里当做数据库连接池。当连接执行数据库操作时,则去调用真正的连接方法,且当连接用完时,将连接的时间清零,放回连接池里。如果大于5个连接数据库时,连接池没有连接时,则创建新的连接放进连接池里,当连接关闭时,如果连接池里已有5个连接,多余的连接如果超过10秒没被使用,则关闭连接。
实现代码如下:
package pool;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Stack;
public class PoolServer {
// 代理连接类
public class ConnProxy {
// 连接属性
private Connection conn;
// 用于计时的属性
private int idle;
public int getIdle() {
return idle;
}
public void setIdle(int idle) {
this.idle = idle;
}
public void setConn(Connection conn) {
this.conn = conn;
}
public Connection getConn() {
return conn;
}
//
public ConnProxy() {
// 设置空闲时间,如果空闲时间超过10秒,则回收,关闭连接
new Thread(() -> {
try {
while (true) {
Thread.sleep(1000);
idle += 1000;
if (idle > 10000) {
synchronized (Object.class) {
if (pool.size() > 5) {
conn.close();
pool.remove(ConnProxy.this);
break;
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
// 使用栈定义数据库连接池
private Stack<ConnProxy> pool = new Stack<ConnProxy>();
// 初始化连接
{
try {
Class.forName("com.mysql.jdbc.Driver");
for (int i = 0; i < 5; i++) {
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/dvdstore", "root", "root");
ConnProxy connProxy = new ConnProxy();
connProxy.setConn(conn);
pool.push(connProxy);
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 获取连接
// 这里加了锁,防止在多线程情况下,会同时去获取连接导致空栈
public synchronized Connection getConn() {
// 如果数据库池里没连接的时候,则创建新的连接放入数据库连接池
if (pool.isEmpty()) {
for (int i = 0; i < 3; i++) {
try {
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/dvdstore", "root",
"root");
ConnProxy connProxy = new ConnProxy();
connProxy.setConn(conn);
pool.push(connProxy);
} catch (Exception e) {
e.printStackTrace();
}
}
}
// ***
// 有连接,则从栈里获取连接
final ConnProxy p = pool.pop();
// 使用java动态代理来代理连接类,拦截close这个方法
return (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), new Class[] { Connection.class },
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
synchronized (Object.class) {
// 当连接close则将连接重新放回数据库连接池
if (method.getName().equals("close")) {
p.setIdle(0);// 被使用过的idle从0开始
pool.push(p);
System.out.println(pool.size());
return null;// 不让其调用真正的close方法
} else {// 当connect不是执行close方法,而是执行其他操作,例如增删改查等操作
Connection conn = p.getConn();
return method.invoke(conn, args);// 调用其真正的connetion方法
}
}
}
});
}
// 测试
public static void main(String[] args) throws Exception {
final PoolServer poolServer = new PoolServer();
for (int i = 0; i < 5; i++) {
new Thread(()-> {
try {
Connection conn = poolServer.getConn();
conn.close();
// System.out.println(conn);
// conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
System.in.read();
}
}