package zzu.maker.util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.LinkedList;
import java.util.Stack;
public class DatabaseConnection {
final static String HOST = "127.0.0.1"; //数据库地址 一般为 local
final static String PORT = "3306"; //端口 一般为3306
final static String DB_NAME = "bbs"; //数据库名字
final static String USERNAME = "root";//数据库用户名
final static String PASSWORD = "123456";//数据库密码
final static String url = "jdbc:mysql://" + HOST + ":" + PORT + "/" + DB_NAME
+ "?useUnicode=true&characterEncoding=utf-8&useSSL=true"; //拼接 url
private static final DatabaseConnection instance = new DatabaseConnection(); //一个连接池
private final int MIN_COUNT = 5; //连接池中最小connection连接数
private final int MAX_COUNT = 10;// 最大connection数
int count = 0; //当前存在 conconnect数
private final Object wait = new Object();// 等待对象
private Stack<Connection> CONN_POOL; // 存储connection连接
private DatabaseConnection() {//构造方法 (设置为私是 单例模式创建连接池)
CONN_POOL = new Stack<Connection>();
try {
Class.forName("com.mysql.jdbc.Driver");//加载jdbc驱动
//连接池中加载最小的连接数
for (int i = 0; i < MIN_COUNT; i++) {
Connection connection = createConnection();
if(connection != null) {
CONN_POOL.add(createConnection());
count++;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static DatabaseConnection getInstance() { //创建连接池方法 (单例模式)
return instance;
}
private static Connection createConnection() { //工厂模式创建connection连接
try {
return DriverManager.getConnection(url, USERNAME, PASSWORD);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
/*
* 我的坑 ,错误的想法
* 关键方法
* 将创建连接的线程锁住,才能保证其他用户不受影响
*
* 模拟
* 用户 1使用 instance.getConnection(),返回链表第5个connection, 然后connection在执行sql语句,然后释放,重新回到连接池中
* 如果在用户1释放后 用户 2使用 instance.getConnection(),返回链表第5个connection, 然后connection在执行sql语句,然后释放,重新回到连接池中
* 如果在用户1释放前 用户2使用 instance.getConnection(),返回链表第4个connection, 然后connection在执行sql语句,然后释放,重新回到连接池中
* 如果始终是释放前 其他用户创建,将5个都用完,
* 而且前五个都没释放,
* 用户6还是instance.getConnection()完成后,后不释放,一直在执行sql语句 此时 count=1,
* 用户7还是instance.getConnection()完成后,后不释放,一直在执行sql语句 ,此时 仍然count=1,
*
* 这样用户可以无限无限的
*
*你好,我是菜鸟,但是我很认真的看了2个小时半,决定睡觉了
有几点疑问~
1、如果前五个用户获取到connection后,都不使用releaseConnection方法,一直在执行sql语句
用户6是instance.getConnection()完成后,一直在执行sql语句,后不释放,此时 count=1,
用户7还是instance.getConnection()完成后,一直在执行sql语句,后不释放, ,此时 仍然count=1,
这样可以无限循环的
2、
如果get后release,我怎么想不到能count超过5的情况
3、如果只用了removeLast,为什么不用栈呢?
我的错误在于 前5个 get后,第6个不会进入 while(CONN_POOL.size() > 0) 循环,会直接 creatConnection的。
*/
/**
* 得到一个connection
*
* @return
*/
public Connection getConnection() {//提供get连接的方法
synchronized (CONN_POOL) {//把线程链表锁住
while(CONN_POOL.size() > 0) {// 判断是否连接池中是否还有未使用的connection
Connection conn = CONN_POOL.pop();
try {
if(conn.isValid(1000)) {
System.out.println("返回一个connection后 count="+count);
return conn;
} else {
count--;
}
} catch (SQLException e) {
e.printStackTrace();
}
}
/*
* CONN_POOL.removeLast() API Removes and returns the last element from this list.
* 线程链表中取出尾部的connection,并且删除。
* 判断取出的线程是否可用 (isValid, jdbc的方法, 会执行代码判断是否未关闭而且可使用
* 可用 返回给用户,不可用,count--,继续执行循环,直到找到可用的connection,或者连接池中的连接全没有
*/
if(count < MAX_COUNT) {
count++;
return createConnection();
}
/*
* 如果比最大连接小,則创建一个新连接返回给用户
*/
synchronized (wait) {
try {
wait.wait();
if(CONN_POOL.size() > 0) {
return CONN_POOL.pop();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/*
* 如果比最大连接大,等待其他连接释放,
* 就是下面的releaseConnection方法,
* 这个方法会将不用的连接给还回来,然后通知 wait对象 ,不用等了,你可以获取连接了
*
* 疑问,为什么不count--
* 因为此时已达到最大连接数,再用的时候永远是在用链表最后一个连接
*/
return null;
}
/**
* 释放connection
* @param connection
*/
public void releaseConnection(Connection connection) {
CONN_POOL.add(connection);
synchronized (wait) {
wait.notify();
}
}
/*
* 获取不用的连接,然后通知需要连接的用户获取连接
*/
}