数据库连接池
Java对于数据库的操作,需要创建相应的数据库连接,connection的创建和释放是耗时的。于是有了数据库连接池,提前创建好若干connection,需要用就去池子里面取,不用了直接放回池子就OK,之后可以供他人使用。
Java常用的数据库连接池有DBCP、C3P0等等
等待/通知经典范式
等待方:
1.获取对象锁
2.while循环判断条件是否满足,不满足则调用对象的wait方法,进入等待队列
3.被通知后依然检查条件是否满足,满足则执行后续逻辑
synchronized (对象){
while(条件判断){
对象.wait();
}
后续代码
}
通知方
1.获取对象锁
2.改变相关条件
3.通知所有等待在该对象上面的线程
synchronized (对象){
改变条件
对象.notifyAll();
}
多线程模拟
ConnectionPool类:维护一个LinkedList,包含获取连接、释放连接两个方法。
public class ConnectionPool {
//利用LinkedList集合存储Connection
private LinkedList<Connection> pool = new LinkedList();
//带参构造,初始化连接数
public ConnectionPool(int initialSize){
for(int i=0;i<initialSize;i++){
pool.add(getConnection());
}
}
//尝试获取连接(millisecond毫秒内获取不到返回null)
public Connection tryFetchConnection(long millisecond){
synchronized (pool){
Connection result = null;
try {
long current = System.currentTimeMillis();
//假如初始millisecond<=0,表示无时间限制
if(millisecond<=0){
while(pool.isEmpty()){
pool.wait();
}
return pool.removeFirst();
}else{
//否则 利用等待/通知范式 在指定时间内去尝试获取连接
//result不为null直接返回,超时直接返回
while (result==null && millisecond>0){
if (pool.isEmpty()){
pool.wait();
millisecond = current+millisecond-System.currentTimeMillis();
}else {
result = pool.removeFirst();
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return result;
}
}
//释放连接,假如Connection不为null,重新加入LinkedList
public void releaseConnection(Connection connection){
synchronized (pool){
if(connection != null){
pool.add(connection);
//同时唤醒所有等待在pool的线程
pool.notifyAll();
}
}
}
//Connection一般都是数据库厂商编写,这里为了方便,通过代理模式创建一个自定义Connection对象
private Connection getConnection(){
Connection connection = (Connection)Proxy.newProxyInstance(ConnectionPool.class.getClassLoader(), new Class[]{Connection.class}, new InvocationHandler() {
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
if(method.getName().equals("commit")){
//在commit里面睡10ms模拟数据库操作
TimeUnit.MILLISECONDS.sleep(10);
}
return null;
}
});
return connection;
}
}
ConnectionTest类:测试类,包含一个静态内部类(ConnectionUser,用来模拟用户获取Connection连接)
public class ConnectionTest {
static ConnectionPool connectionPool = new ConnectionPool(10);
//保证所有用户线程的run方法同时开始
static CountDownLatch start;
//保证所有用户线程执行完毕,再打印got(成功获取数量)和unGot(未成功获取数量)
static CountDownLatch end;
public static void main(String[] args) {
int threadCount = 30;
int count = 20;
AtomicInteger got = new AtomicInteger(0);
AtomicInteger unGot = new AtomicInteger(0);
start = new CountDownLatch(1);
end = new CountDownLatch(threadCount);
for(int i=0;i<threadCount;i++){
new Thread(new ConnectionUser(got,unGot,count)).start();
}
start.countDown();
try {
end.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
//最后通过打印两个值,可以查看成功获取和未成功获取的数量
System.out.println("got:"+got);
System.out.println("unGot:"+unGot);
}
//模拟用户线程
static class ConnectionUser implements Runnable{
AtomicInteger got;
AtomicInteger unGot;
int count;
public ConnectionUser(AtomicInteger got,AtomicInteger unGot,int count){
this.got = got;
this.unGot = unGot;
this.count = count;
}
@Override
public void run() {
try {
//等待所有用户线程初始化完毕
start.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
//一个线程获取count次连接
while (count>0){
//尝试获取连接,最大等待时间1000ms
Connection connection = connectionPool.tryFetchConnection(1000);
if(connection != null){
try {
//获取到got加1,利用commit睡10ms
got.incrementAndGet();
connection.createStatement();
connection.commit();
} catch (SQLException e) {
e.printStackTrace();
}finally {
//释放连接,将connection放回LinkedList
connectionPool.releaseConnection(connection);
}
}else {
//未获取到 unGot加1
unGot.incrementAndGet();
}
count--;
}
//countDownLatch数量减一,代表一个用户线程执行完毕
end.countDown();
}
}
}
本文深入探讨Java中数据库连接池的实现原理与实践,通过自定义ConnectionPool类,采用等待/通知经典范式,实现线程安全的连接获取与释放。并通过ConnectionTest类模拟多线程环境下连接池的高效运作。
3371

被折叠的 条评论
为什么被折叠?



