java并发实战:连接池实现
池化技术简介
在我们使用数据库的过程中,我们往往使用数据库连接池而不是直接使用数据库连接进行操作,这是因为每一个数据库连接的创建和销毁的代价是昂贵的,而池化技术则预先创建了资源,这些资源是可复用的,这样就保证了在多用户情况下只能使用指定数目的资源,避免了一个用户创建一个连接资源,造成程序运行开销过大。关于Java并发编程的总结和思考
连接池实现原理
这里只实现一个简易的连接池,更多复杂的需求可根据该连接池进行改进,该连接池主要参数如下:
- 一个繁忙队列busy
- 一个空闲队列idle
- 连接池最大活动连接数maxActive
- 连接池最大等待时间maxWait
- 连接池的活动连接数activeSize
程序流程图如下:

代码实现
泛型接口ConnectionPool.java
public interface ConnectionPool<T> {
/**
* 初始化池资源
* @param maxActive 池中最大活动连接数
* @param maxWait 最大等待时间
*/
void init(Integer maxActive, Long maxWait);
/**
* 从池中获取资源
* @return 连接资源
*/
T getResource() throws Exception;
/**
* 释放连接
* @param connection 正在使用的连接
*/
void release(T connection) throws Exception;
/**
* 释放连接池资源
*/
void close();
}
以zookeeper为例,实现zookeeper连接池,ZookeeperConnectionPool.java
public class ZookeeperConnectionPool implements ConnectionPool<ZooKeeper> {
//最大活动连接数
private Integer maxActive;
//最大等待时间
private Long maxWait;
//空闲队列
private LinkedBlockingQueue<ZooKeeper> idle = new LinkedBlockingQueue<>();
//繁忙队列
private LinkedBlockingQueue<ZooKeeper> busy = new LinkedBlockingQueue<>();
//连接池活动连接数
private AtomicInteger activeSize = new AtomicInteger(0);
//连接池关闭标记
private AtomicBoolean isClosed = new AtomicBoolean(false);
//总共获取的连接记数
private AtomicInteger createCount = new AtomicInteger(0);
//等待zookeeper客户端创建完成的计数器
private static ThreadLocal<CountDownLatch> latchThreadLocal = ThreadLocal.withInitial(() -> new CountDownLatch(1));
public ZookeeperConnectionPool(Integer maxActive, Long maxWait) {
this.init(maxActive, maxWait);
}
@Override
public void init(Integer maxActive, Long maxWait) {
this.maxActive = maxActive;
this.maxWait = maxWait;
}
@Override
public ZooKeeper getResource() throws Exception {
ZooKeeper zooKeeper;
Long nowTime = System.currentTimeMillis();
final CountDownLatch countDownLatch = latchThreadLocal.get();
//空闲队列idle是否有连接
if ((zooKeeper = idle.poll()) == null) {
//判断池中连接数是否小于maxActive
if (activeSize.get() < maxActive) {
//先增加池中连接数后判断是否小于等于maxActive
if (activeSize.incrementAndGet() <= maxActive) {
//创建zookeeper连接
zooKeeper = new ZooKeeper("localhost", 5000, (watch) -> {
if (watch.getState() == Watcher.Event.KeeperState.SyncConnected) {
countDownLatch.countDown();
}
});
countDownLatch.await();
System.out.println("Thread:" + Thread.currentThread().getId() + "获取连接:" + createCount.incrementAndGet() + "条");
busy.offer(zooKeeper);
return zooKeeper;
} else {
//如增加后发现大于maxActive则减去增加的
activeSize.decrementAndGet();
}
}
//若活动线程已满则等待busy队列释放连接
try {
System.out.println("Thread:" + Thread.currentThread().getId() + "等待获取空闲资源");
Long waitTime = maxWait - (System.currentTimeMillis() - nowTime);
zooKeeper = idle.poll(waitTime, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
throw new Exception("等待异常");
}
//判断是否超时
if (zooKeeper != null) {
System.out.println("Thread:" + Thread.currentThread().getId() + "获取连接:" + createCount.incrementAndGet() + "条");
busy.offer(zooKeeper);
return zooKeeper;
} else {
System.out.println("Thread:" + Thread.currentThread().getId() + "获取连接超时,请重试!");
throw new Exception("Thread:" + Thread.currentThread().getId() + "获取连接超时,请重试!");
}
}
//空闲队列有连接,直接返回
busy.offer(zooKeeper);
return zooKeeper;
}
@Override
public void release(ZooKeeper connection) throws Exception {
if (connection == null) {
System.out.println("connection 为空");
return;
}
if (busy.remove(connection)){
idle.offer(connection);
} else {
activeSize.decrementAndGet();
throw new Exception("释放失败");
}
}
@Override
public void close() {
if (isClosed.compareAndSet(false, true)) {
idle.forEach((zooKeeper) -> {
try {
zooKeeper.close();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
busy.forEach((zooKeeper) -> {
try {
zooKeeper.close();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}
}
测试用例
这里创建20个线程并发测试连接池,Test.java
public class Test {
public static void main(String[] args) throws Exception {
int threadCount = 20;
Integer maxActive = 10;
Long maxWait = 10000L;
ZookeeperConnectionPool pool = new ZookeeperConnectionPool(maxActive, maxWait);
CountDownLatch countDownLatch = new CountDownLatch(20);
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
countDownLatch.countDown();
try {
countDownLatch.await();
ZooKeeper zooKeeper = pool.getResource();
Thread.sleep(2000);
pool.release(zooKeeper);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
while (true){
}
}
}
来源:https://segmentfault.com/a/1190000017926611
java并发实战:连接池实现的更多相关文章
- Java基础-DBCP连接池(BasicDataSource类)详解
Java基础-DBCP连接池(BasicDataSource类)详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 实际开发中“获得连接”或“释放资源”是非常消耗系统资源的两个过程 ...
- 【转帖】置高并发jdbc连接池
简单的MySQL连接池 <Resource type="javax.sql.DataSource" name="jdbc/TestDB" factory= ...
- Java并发编程——线程池的使用
在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统 ...
- java并发:线程池、饱和策略、定制、扩展
一.序言 当我们需要使用线程的时候,我们可以新建一个线程,然后显式调用线程的start()方法,这样实现起来非常简便,但在某些场景下存在缺陷:如果需要同时执行多个任务(即并发的线程数量很多),频繁地创 ...
- Java为什么使用连接池
一.简介 动态Web站点往往用数据库存储的信息生成Web页面,每一个页面请求导致一次数据库访问.连接数据库不仅要开销一定的通信和内存资源,还必须完成用户验证.安全上下文配置这类任务,因为往往成为最为耗 ...
- java JDBC (八) 连接池 DBCP
package cn.sasa.demo1; import javax.sql.DataSource; import org.apache.commons.dbcp2.BasicDataSource; ...
- Java 自定义FTP连接池
转自:https://blog.csdn.net/eakom/article/details/79038590 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn ...
- java使用DBCP连接池创建工具类
1.说明 java中有个扩展包 javax下面有个DataResource的接口 javax.sql.DataResource 该接口定义了连接池的方法规范 而DBCP框架有apache公司开发,他 ...
- Java并发——ThreadPoolExecutor线程池解析及Executor创建线程常见四种方式
前言: 在刚学Java并发的时候基本上第一个demo都会写new Thread来创建线程.但是随着学的深入之后发现基本上都是使用线程池来直接获取线程.那么为什么会有这样的情况发生呢? new Thre ...
随机推荐
- Codeforces Round #413 A. Carrot Cakes
A. Carrot Cakes time limit per test 1 second memory limit per test 256 megabytes In some game ...
- 【Python】卸载完Python3 之后 Python2 无法打开IDLE
安装官方的Python带Idle但是却无法打开,百度谷歌了几种解决方法,加上自己的实际境况予以解决. 我的python是直接安装在C盘下的. 1.首先是设置环境变量: Path=C:\Python27 ...
- keytool使用方法
获取KeyHash三步骤 1.openssl-link 2.jdk-link 3.KeySotre的HashKey获取方式: keytool.exe -exportcert -alias mia -k ...
- switch case 忘记 break
昨天纠结了很久的问题,一直找不到原因,早上发现是一个低级错误: private void selectButton(int id) { switch (id) { case 0: recommend_ ...
- 二十、滑动开关css
如上图所示的图片,如何通过css实现呢? 下面咱们慢慢尝试: html: <div class="togglePosition"> <label class=&q ...
- chrome 插件开发学习
http://blog.csdn.net/my_business/article/details/7711525
- Windows下Python2与Python3两个版本共存的方法详解
来源:http://www.jb51.net/article/105311.htm 这篇文章主要介绍了Windows下Python2与Python3两个版本共存的方法,文中介绍的很详细,对大家具有一定 ...
- Oracle归档模式和非归档模式的区别
一.查看oracle数据库是否为归档模式: Sql代码1.select name,log_mode from v$database; NAME LOG_MODE ------------------ ...
- leetcode-distinct sequences
Solution: when see question about two strings , DP should be considered first. We can abstract this ...
- window 服务注册、卸载
1.以管理员身份打开 命令窗口 2.服务注册命令:sc create myServer binpath= path 3.服务卸载命令:sc delete myServer Topshelf 服务安装 ...