Java 模拟数据库连接池的实现
前面学习过等待 - 通知机制,现在我们在其基础上添加一个超时机制,模拟从连接池中获取、使用和释放连接的过程。客户端获取连接的过程被设定为等待超时模式,即如果在 1000 毫秒内无法获取到可用连接,将会返回给客户端一个 null。设定连接池的大小为 10 个,然后通过调节客户端的线程数来模拟无法获取连接的场景
由于 java.sql.Connection 只是一个接口,最终实现是由数据库驱动提供方来实现,考虑到本例只是演示,我们通过动态代理构造一个 Connection,该 Connection 的代理仅仅是在调用 commit() 方法时休眠 100 毫秒
public class ConnectionDriver {
static class ConnectionHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("commit".equals(method.getName())) {
TimeUnit.MICROSECONDS.sleep(100);
}
return null;
}
}
/**
* 创建一个 Connection 的代理,在 commit 时休眠 100 毫秒
*/
public static Connection createConnection() {
return (Connection) Proxy.newProxyInstance(ConnectionDriver.class.getClassLoader(),
new Class<?>[]{Connection.class}, new ConnectionHandler());
}
}
接下来是线程池的实现。本例通过一个双向队列来维护连接,调用方需要先调用 fetchConnection(long) 方法来指定在多少毫秒内超时获取连接,当连接使用完成后,需要调用 releaseConnection(Connection) 方法将连接放回线程池
public class ConnectionPool {
private final LinkedList<Connection> pool = new LinkedList<>();
public ConnectionPool(int initialSize) {
// 初始化连接的最大上限
if (initialSize > 0) {
for (int i = 0; i < initialSize; i++) {
pool.addLast(ConnectionDriver.createConnection());
}
}
}
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();
} else {
long future = System.currentTimeMillis() + mills;
long remaining = mills;
while (pool.isEmpty() && remaining > 0) {
pool.wait(remaining);
remaining = future - System.currentTimeMillis();
}
Connection result = null;
if (!pool.isEmpty()) {
result = pool.removeFirst();
}
return result;
}
}
}
}
最后编写一个用于模拟客户端获取连接的示例,该示例将模拟多个线程同时从连接池获取连接,并记录总尝试获取数、获取成功数和获取失败数
public class ConnectionPoolTest {
static ConnectionPool pool = new ConnectionPool(10);
static CountDownLatch start = new CountDownLatch(1);
static CountDownLatch end;
public static void main(String[] args) throws InterruptedException {
// 线程数量
int threadCount = 200;
end = new CountDownLatch(threadCount);
int count = 20;
AtomicInteger got = new AtomicInteger();
AtomicInteger notGot = new AtomicInteger();
for (int i = 0; i < threadCount; i++) {
Thread thread = new Thread(new ConnectionRunner(count, got, notGot), "ConnectionRunnerThread");
thread.start();
}
start.countDown();
end.await();
System.out.println("total invoke : " + (threadCount * count));
System.out.println("got connection : " + got);
System.out.println("not got connection : " + notGot);
}
static class ConnectionRunner implements Runnable {
int count;
AtomicInteger got;
AtomicInteger notGot;
public ConnectionRunner(int count, AtomicInteger got, AtomicInteger notGot) {
this.count = count;
this.got = got;
this.notGot = notGot;
}
@Override
public void run() {
try {
start.await();
} catch (Exception e) {
e.printStackTrace();
}
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();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
count--;
}
}
end.countDown();
}
}
}
笔者设置线程数量为 200 时,得出结果如下

当设置为 500 时,得出结果如下,当然具体结果根据机器性能而异

可见,随着客户端线程数的增加,客户端出现超时无法获取连接的比率不断升高。这种等待超时模式能保证程序出问题时,线程不会一直运行,而是按时返回,并告知客户端获取连接出现问题。数据库连接池的实际也可以应用到其他资源获取的场景,针对昂贵资源的获取都应该加以限制
Java 模拟数据库连接池的实现的更多相关文章
- java配置数据库连接池的方法步骤
java配置数据库连接池的方法步骤 java配置数据库连接池的方法步骤,需要的朋友可以参考一下 先来了解下什么是数据库连接池数据库连接池技术的思想非常简单,将数据库连接作为对象存储在一个Vecto ...
- Java第三方数据库连接池库-DBCP-C3P0-Tomcat内置连接池
连接池原理 数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”.预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去.我们可以通过设定连接池 ...
- Java中数据库连接池原理机制的详细讲解以及项目连接数据库采用JDBC常用的几种连接方式
连接池的基本工作原理 1.基本概念及原理 由上面的分析可以看出,问题的根源就在于对数据库连接资源的低效管理.我们知道,对于共享资源,有一个很著名的设计模式:资源池(Resource Pool).该模式 ...
- java实现数据库连接池
package com.kyo.connection; import java.sql.Connection; import java.sql.DatabaseMetaData; import jav ...
- Java jdbc数据库连接池总结!(转)
1. 引言 近年来,随着Internet/Intranet建网技术的飞速发展和在世界范围内的迅速普及,计算机 应用程序已从传统的桌面应用转到Web应用.基于B/S(Browser/Server)架构的 ...
- 【Java】数据库连接池技术
JDBC的问题 在程序中,我们经常要建立与数据库的连接,之后再关闭这个连接.我们知道,数据库连接对象的创建是比较消耗系统性能的,这些频繁的操作势必会消耗大量的系统资源.因此我们需要采用更高效的数据库访 ...
- java通过数据库连接池链接oracle
开发工具:Eclipse J2EE 3.6 运行环境:jdk1.6 部署环境:Tomcat7 数据库连接池用的是dbcp,网上download下来的三个jar包. 把数据库连接池包和jdbc的包放到t ...
- java常用数据库连接池 (DBCP、c3p0、Druid) 配置说明
1. 引言 1.1 定义 数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出.对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标.数据库 ...
- java之数据库连接池-dbcp&c3p0&dbutils
介绍 因为数据库连接对象的创建比较消耗性能,所以可以在应用程序启动时就在内存中开辟一片空间(集合)存放多个数据库连接对象,后面需要连接时直接从该空间中取而不用新创建:使用完毕后归还连接(将连接重新放回 ...
- Java中数据库连接池原理机制的详细讲解
连接池的基本工作原理 1.基本概念及原理 由上面的分析可以看出,问题的根源就在于对数据库连接资源的低效管理.我们知道,对于共享资源,有一个很著名的设计模式:资源池(Resource Pool).该模式 ...
随机推荐
- 文心一言 VS 讯飞星火 VS chatgpt (135)-- 算法导论11.3 1题
一.用go语言,假设我们希望查找一个长度为 n 的链表,其中每一个元素都包含一个关键字k并具有散列值h(k).每一个关键字都是长字符串.那么在表中查找具有给定关键字的元素时,如何利用各元素的散列值呢? ...
- 关于Anolis8/Centos8系统重启后ip地址丢失的原因
关于Anolis8/Centos8系统重启后ip地址丢失的原因 #.今天把之前在VMware安装的Anolis8系统重启了,启动之后发现Xshell连接不上.在VMware上登录后执行ip a命令发现 ...
- Linux速查备忘手册
速查手册 网盘文档PDF资料: 链接: https://pan.baidu.com/s/111rqKfPaAiOHSHDo1SnckA 提取码: mhkv 1. 2. 3. 4. 5. ...
- Hdu4742 (CDQ分治)
题意:给出n个三维点对(x,y,z),可随意排列,求三维非严格最长上升子序列长度和最长上升子序列数量. 输入格式:第一行为一整数T表示用例组数,每组用例第一行为一整数n表示点数,之后n行每行三个整数x ...
- ARM汇编指令实验
题目 地址为0x40008000起始的内存中存放了20个无符号的8位整数,请编写ARM汇编程序实现如下功能: 采用冒泡法将以上内存中的数据按照从小到大的顺序排列. 注意:在验收实验时,需要自己把具体的 ...
- springBoot——整合junit
spring整合junit复习 springBoot整合junit package com.example.springboot_04; import com.example.springboot_0 ...
- Spring Cloud Seata系列:基于AT模式实现分布式事务
目录 前提 Seata的AT模型 流程梳理 一阶段: 二阶段-回滚 二阶段-提交 脏写问题 写隔离 读隔离 优缺点 AT与XA的区别 实现AT模式 https://seata.io/zh-cn/doc ...
- 34. 干货系列从零用Rust编写负载均衡及代理,异步测试在Rust中的实现
wmproxy wmproxy已用Rust实现http/https代理, socks5代理, 反向代理, 静态文件服务器,四层TCP/UDP转发,七层负载均衡,内网穿透,后续将实现websocket代 ...
- dtd的三种引入方式
- Python——第一章:数据类型介绍
数据类型: 区分不同的数据.不同的数据类型应该有不同的操作 数字: 做加减乘除+-*/ 整数,int 小数,float a= 10 #整数 b = 20 print(a + b) #加法运算 c = ...