使用享元模式和多线程支持来保护数据库连接池的线程安全:
连接池的属性和方法:
大小
连接对象
状态
构造方法初始化
借连接
归还连接
import lombok.extern.slf4j.Slf4j;
import java.sql.*;
import java.util.Date;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerArray;
@Slf4j(topic = "c.Pool")
public class Pool {
//1连接池的大小
private final int poolSize;//简单实现,因此设置为final,一旦设置值则不可改变---->实际数据库连接的大小是可变的
//2连接对象
private Connection[] connections;
//3连接状态数组:0表示空闲,1表示繁忙
private AtomicIntegerArray states;//多线程都想更改状态时会存在线程安全问题,因此需要使用线程安全的数组
//4构造方法初始化:对属性的初始化
public Pool(int poolSize){
this.poolSize=poolSize;
this.connections=new Connection[poolSize];
this.states=new AtomicIntegerArray(new int[poolSize]);//状态一开始为0表示空闲,不必单独初始化
for(int i=0;i<poolSize;i++){
connections[i]= (Connection) new MockConnection("连接"+(i+1));//创建连接对象
}
}
//5借连接:基本逻辑是:检查是否有空闲连接(查看状态数组)--->如果为0,表示有空闲,直接返回该连接,但在这之前要将状态由0改为1,由于
//状态数组是原子整数数组,因此需要配合while(true)循环,不断尝试----乐观锁机制
public Connection borrow(){
while(true){
for(int i=0;i<poolSize;i++) {
//检查是否有空闲连接----有空闲连接
if (states.get(i) == 0) {
if (states.compareAndSet(i, 0, 1)) {//将状态由0改为1,多线程下存在线程安全问题,使用cas
log.debug("borrow{}", connections[i]);
return (Connection) connections[i];//返回一个连接对象
}
}
}
//没有空闲连接--->当前线程进入等待
synchronized (this){
try{
log.debug("wait...");
this.wait(); //进入等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//6归还连接----j>检查要归还的连接是否是该连接池中的连接(遍历连接池中的第i个元素是否与参数传递的连接对象是同一个),
// 若是则将状态由1改为0,并通知其他线程,然后跳出循环
public void free(Connection conn){
for (int i=0;i<poolSize;i++){
if(connections[i]==conn){
states.set(i,0);//不必使用cas,因为归还连接的线程就是线程的持有者,所以只有这个线程拥有这个连接的使用权,不会发生竞争
synchronized (this){
log.debug("free{}",conn);
this.notifyAll();
}
break;
}
}
}
}
class MockConnection implements Connection{
public MockConnection(String s) {
}
}