public class Test14 {
public static void main(String[] args) {
ConnectionPool pool = new ConnectionPool(3);
for (int i = 0; i < 4; i++) {
new Thread(() -> {
Connection connection = pool.take();
// 模拟使用的时间
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
pool.free(connection);
}).start();
}
}
}
@Slf4j
// 自己手写一个简易版本的数据库连接池
class ConnectionPool {
/**
* 什么是连接池呢?
* 顾名思义,就是一个池子,里面有许多的 Connection 连接对象。
* 有啥好处呢?
* 可以重用 Connection 连接对象,避免大量的创建,关闭 Connection 连接对象(这些非常耗时,影响效率的操作)
*/
// 一个存储连接对象的容器,
private Connection[] connections;
// 一个记录连接对象状态的数组,但是为了考虑并发安全。所以要使用线程安全的。
// 0 代表:空闲;1 代表:正在被使用中
private AtomicIntegerArray states;
// Connection 连接对象的数量
private int capacity;
// 连接池的初始化
public ConnectionPool(int capacity) {
connections = new Connection[capacity];
states = new AtomicIntegerArray(new int[capacity]);
this.capacity = capacity;
for (int i = 0; i < capacity; i++) {
connections[i] = new MyConnection();
}
}
// 尝试获取一个 Connection 连接对象
public Connection take() {
// 为了避免出现线程安全问题,这里使用 synchronized 锁,也可以使用 cas
while (true) {
for (int i = 0; i < connections.length; i++) {
if (states.get(i) == 0) {
Connection connection = connections[i];
if (states.compareAndSet(i, 0, 1)) {
log.debug("{} 连接对象被线程拿走去使用了", connection);
return connection;
}
}
}
/**
* 整个容器都遍历完了,还是没有找到空闲的 Connection 连接对象
* 这里有很多的做法:
* 1.可以一直死等,等待有 Connection 连接对象被归还,然后被唤醒。再重新遍历容器,寻找空闲的 Connection 连接对象
* 2.也可以有超时时间的等待,不是一直死等
*/
synchronized (this) {
try {
log.debug("线程没找到空闲的连接对象,陷入了等待");
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 归还 Connection 连接对象
public void free(Connection connection) {
for (int i = 0; i < connections.length; i++) {
if (connection == connections[i]) {
// 将这个连接的对象设置为空闲,这样别的线程就可以拿到了
log.debug("归还了连接对象,{}", connection);
// 这里直接 set 就行了,不用使用 cas 了。
// 因为只会有一个线程拿到这个 Connection 连接对象,所以不会有线程安全的问题
states.set(i, 0);
// 记得唤醒其他正在等待 Connection 连接对象的线程
/**
* 调用wait(),notify()和notifyAll()的线程在调用这些方法前必须"拥有"对象的锁。
* 当前的线程不是此对象锁的所有者,却调用该对象的notify(),notify(),wait()方法时抛出 IllegalMonitorStateException 异常。
*/
synchronized (this) {
log.debug("唤醒其他等待的线程");
this.notifyAll();
}
break;
}
}
}
}
// 这里为了方便,就自己实现一个 Connection 连接对象,来假设是数据库厂商实现的
class MyConnection implements Connection {
@Override
public Statement createStatement() throws SQLException {
return null;
}
// ...
}
自己手写一个简易版本的数据库连接池【Java】【详细的注解,有思考过程和知识点】
于 2022-12-28 20:55:51 首次发布