一、设计数据库连接池
将数据库连接池可以作为一个容器,在这个容器里边含有Connection(JDBC接口),可以将Connection作为一个管道,每一次线程获取Connection(获取管道)后进行数据库操作。
1、Connection的存储结构选择Linkedlist
2、线程释放管道:从Linkedlist删除该管道,通过wait-notify唤醒阻塞线程
3、线程获取管道:选择两种模式:无超时等待模式、超时等待模式,无超时等待每一个线程获取连接池锁之后,直接等待获取管道,这就造成了资源的独占,增加负担;超时模式,在获得锁之后,首先在一定的时间限制内则循环等待,如果在等待期间,获得到管道则返回,超过限定时间无法获得管道返回null;
/**
* 1、超时机制 2、wait-notify
*
* @author 12803
*
*/
public class ConnectionPool {
LinkedList<Connection> pool = new LinkedList<>();
public ConnectionPool(int initialValue) {
for (int i = 0; i < initialValue; i++) {
// Connection conn =
// DriverManager.getConnection(DbconfigXML.jdbcUrl,
// DbconfigXML.jdbcName,
// DbconfigXML.jdbcPassword);
Connection conn = ConnectionDriver.createConnection();
pool.addFirst(conn);
}
}
/**
* 数据库连接池释放一个连接
*
* @param conn
*/
public void releaseConn(Connection conn) {
synchronized (pool) {
pool.addFirst(conn);
pool.notifyAll();
}
}
public Connection getConnectionfromPool(long mills) throws InterruptedException {
synchronized (pool) {
// 无超时等待
if (mills <= 0) {
while (pool.isEmpty()) {
pool.wait();
}
return pool.removeFirst();
} else {
// 延时等待
long future = System.currentTimeMillis() + mills;
long remain = mills;
while (pool.isEmpty() && remain > 0) {
pool.wait(remain);
remain = future - System.currentTimeMillis();
}
Connection result = null;
if (!pool.isEmpty()) {
result = pool.removeFirst();
}
return result;
}
}
}
}
二、动态代理模拟数据库驱动
1、继承InvocationHandler,用做代理对象(proxy)调用处理程序,正常情况在内部需要绑定真实对象。
2、通过动态代理产生Connection
3、现阶段对动态代理理解不太深,参考博客:https://blog.csdn.net/yaomingyang/article/details/80981004
public class ConnectionDriver {
static class Handler implements InvocationHandler {
@Override
/**
* proxy:代理类代理的真实代理对象com.sun.proxy.$Proxy0
* method:我们所要调用某个对象真实的方法的Method对象
* args:指代代理对象方法传递的参数
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("commit")) {
TimeUnit.MILLISECONDS.sleep(100);
}
return null;
}
}
public static final Connection createConnection() {
/**
* ConnectionDriver.class.getClassLoader():通过ConnectionDriver类的加载器加载
* new Class<?>[] {Connection.class}:加载Connection类
* new Handler():关联的调用处理程序
*/
Connection connection = (Connection) Proxy.newProxyInstance(ConnectionDriver.class.getClassLoader(),
new Class<?>[] {Connection.class}, new Handler());
return connection;
}
}
三、多线程测试
1、设置CountDownLatch start:保证所有线程同时开始(设置为1)
2、设置CountDownLatch end:保证所有线程同时结束(设置为线程数)
3、AtomicInteger get = new AtomicInteger();获取到Connection的线程数
AtomicInteger Notget = new AtomicInteger();未获取到Connection的线程数
public class ConnectionPoolTest {
static ConnectionPool pool = new ConnectionPool(10);
// 保证所有线程同时开始
static CountDownLatch start = new CountDownLatch(1);
// 保证线程同时结束
static CountDownLatch end;
public static void main(String[] args) {
int ThreadCount = 15;
end = new CountDownLatch(ThreadCount);
int count = 20;//表明每一个线程的获取次数
AtomicInteger get = new AtomicInteger();
AtomicInteger Notget = new AtomicInteger();
System.out.println("准备子线程");
for (int i = 0; i < ThreadCount; i++) {
Thread thread = new Thread(new MyRunner(count, get, Notget), "ConnectionRunnerThread_"+i);
thread.start();
}
start.countDown();// 让所有线程开始运行
try {
end.await();// 主线程等待所有子线程完成
System.out.println("所有子线程结束");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("total invoke:" + (ThreadCount * count));//总共有ThreadCount个线程,每一个线程获取count次
System.out.println("Get:" + get);
System.out.println("Not Get:" + Notget);
}
static class MyRunner implements Runnable {
int count;
AtomicInteger get;
AtomicInteger Notget;
public MyRunner(int count, AtomicInteger get, AtomicInteger Notget) {
this.count = count;
this.get = get;
this.Notget = Notget;
}
@Override
public void run() {
try {
start.await();// 等待主线程信号,启动子线程
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("子线程:"+Thread.currentThread().getName());
while (count > 0) {
try {
Connection connection = pool.getConnectionfromPool(100);
if (connection != null) {
try {
connection.createStatement();
connection.commit();
} finally {
pool.releaseConn(connection);
get.incrementAndGet();
}
} else {
Notget.incrementAndGet();
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
count--;
}
}
end.countDown();// 当前子线程运行结束
}
}
}
四、运行结果分析:
1、其中在测试代码中ThreadCount*count含义:假定一个数据库,有ThreadCount个用户,每个用户访问count次
准备子线程
子线程:ConnectionRunnerThread_10
子线程:ConnectionRunnerThread_11
子线程:ConnectionRunnerThread_8
子线程:ConnectionRunnerThread_9
子线程:ConnectionRunnerThread_7
子线程:ConnectionRunnerThread_6
子线程:ConnectionRunnerThread_4
子线程:ConnectionRunnerThread_5
子线程:ConnectionRunnerThread_0
子线程:ConnectionRunnerThread_1
子线程:ConnectionRunnerThread_3
子线程:ConnectionRunnerThread_13
子线程:ConnectionRunnerThread_2
子线程:ConnectionRunnerThread_14
子线程:ConnectionRunnerThread_12
所有子线程结束
total invoke:300
Get:214
Not Get:86
五、结论
当客户端线程逐步增加,客户端无法获取连接的比率增加,不会让客户端线程一直挂在获取联机的操作上,而是按时返回,给用户告知错误,是系统的保护机制。