众所周知,Java的Object对象提供的,wait()和notify()/notifyAll()等接口是并发编程的重要组成部分。它们对多线程之间的协作起了非常重要的作用,实际开发中也有很多场景可以采用。废话少说,今天我们就用此机制来模拟实现一个jdbc支持等待超时模式的连接池。
一、模拟实现一个数据库连接接口//类说明:空实现一个Connection接口(因为重点不在这里,所以以下接口中的方法只做简单处理)
public class SqlConnectImpl implements Connection{
/*拿一个数据库连接*/
public static final Connection fetchConnection(){
return new SqlConnectImpl();
}
@Override
public boolean isWrapperFor(Class> arg0) throws SQLException {
// TODO Auto-generated method stub
return false;
}
//因为重点不在这里,所以这里省略其它接口...
}
二、实现数据库等待超时连接池的核心方法
//类说明:连接池的实现
DBPool {
//模拟:数据库连接池
LinkedList pool = LinkedList()(initialSize) {
if(initialSize > ) {
for(int i = 0;i
pool.addLast(SqlConnectImpl.fetchConnection());
}
}
}
//连接池:释放连接,通知其他线程
public void releaseConnection(Connection connection) {
if (connection != null) {
synchronized (pool){
pool.addLast(connection);
pool.notifyAll();
}
}
}
//连接池:获取连接使用
public Connection fetchConnection(long mills) throws InterruptedException {
synchronized (pool){
//未设置超时,直接获取
if(mills <0){
while (pool.isEmpty()){
pool.wait();
}
return pool.removeFirst();
}
//设置超时
long future = System.currentTimeMillis()+mills;/*超时时刻*/
long remaining = mills;
while (pool.isEmpty() && remaining > 0){
pool.wait(remaining);
//唤醒一次:重新计算等待时长
remaining = future - System.currentTimeMillis();
}
Connection connection = null;
if(!pool.isEmpty()){
connection = pool.removeFirst();
}
return connection;
}
}
}
三、多线程并发模式下对连接池的访问
//类说明:数据库连接池测试类
public class DBPoolTest {
static DBPool pool = new DBPool(10);
// 控制器:控制main线程将会等待所有Woker结束后才能继续执行
static CountDownLatch end;
public static void main(String[] args) throws Exception {
// 线程数量
int threadCount = 50;
end = new CountDownLatch(threadCount);
int count = 20;//每个线程的操作次数
AtomicInteger got = new AtomicInteger();//计数器:统计可以拿到连接的线程
AtomicInteger notGot = new AtomicInteger();//计数器:统计没有拿到连接的线程
for (int i = 0; i
Thread thread = new Thread(new Worker(count, got, notGot),
"worker_"+i);
thread.start();
}
end.await();// main线程在此处等待
System.out.println("总共尝试了: " + (threadCount * count));
System.out.println("拿到连接的次数: " + got);
System.out.println("没能连接的次数: " + notGot);
}
static class Worker implements Runnable {
int count;
AtomicInteger got;
AtomicInteger notGot;
public Worker(int count, AtomicInteger got,
AtomicInteger notGot) {
this.count = count;
this.got = got;
this.notGot = notGot;
}
public void run() {
while (count > 0) {
try {
// 从线程池中获取连接,如果1000ms内无法获取到,将会返回null
// 分别统计连接获取的数量got和未获取到的数量notGot
Connection connection = pool.fetchConnection(1000);
if (connection != null) {
try {
connection.createStatement();
connection.commit();
} finally {
pool.releaseConnection(connection);
got.incrementAndGet();
}
} else {
notGot.incrementAndGet();
System.out.println(Thread.currentThread().getName()
+"等待超时!");
}
} catch (Exception ex) {
} finally {
count--;
}
}
end.countDown();
}
}
}
四、测试结果报告
五、结束语
总结:1) 对wait()、notify()、notifyAll()等方法使用时须用,synchronized关键包裹(对象、方法或块)均可, 不包裹运行中必报错;
2) 线程在执行wait()方法在会自动释放持有的锁;
3) 线程在执行notify()或notifyAll()后,不会立即释放该线程持有的锁资源,只有在synchronized包裹的语句块或方法执行完毕后才会释放;
4) 采用notify()方法唤醒时只会随机唤醒一个线程,在多唤醒条件下不适用此方法。推荐使用notifyAll()唤醒所有与锁对象相关的所有线程;