池化技术简介

在我们使用数据库的过程中,我们往往使用数据库连接池而不是直接使用数据库连接进行操作,这是因为每一个数据库连接的创建和销毁的代价是昂贵的,而池化技术则预先创建了资源,这些资源是可复用的,这样就保证了在多用户情况下只能使用指定数目的资源,避免了一个用户创建一个连接资源,造成程序运行开销过大。关于Java并发编程的总结和思考

连接池实现原理

这里只实现一个简易的连接池,更多复杂的需求可根据该连接池进行改进,该连接池主要参数如下:

  1. 一个繁忙队列busy
  2. 一个空闲队列idle
  3. 连接池最大活动连接数maxActive
  4. 连接池最大等待时间maxWait
  5. 连接池的活动连接数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并发实战:连接池实现的更多相关文章

  1. Java基础-DBCP连接池(BasicDataSource类)详解

    Java基础-DBCP连接池(BasicDataSource类)详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 实际开发中“获得连接”或“释放资源”是非常消耗系统资源的两个过程 ...

  2. 【转帖】置高并发jdbc连接池

    简单的MySQL连接池 <Resource type="javax.sql.DataSource" name="jdbc/TestDB" factory= ...

  3. Java并发编程——线程池的使用

    在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统 ...

  4. java并发:线程池、饱和策略、定制、扩展

    一.序言 当我们需要使用线程的时候,我们可以新建一个线程,然后显式调用线程的start()方法,这样实现起来非常简便,但在某些场景下存在缺陷:如果需要同时执行多个任务(即并发的线程数量很多),频繁地创 ...

  5. Java为什么使用连接池

    一.简介 动态Web站点往往用数据库存储的信息生成Web页面,每一个页面请求导致一次数据库访问.连接数据库不仅要开销一定的通信和内存资源,还必须完成用户验证.安全上下文配置这类任务,因为往往成为最为耗 ...

  6. java JDBC (八) 连接池 DBCP

    package cn.sasa.demo1; import javax.sql.DataSource; import org.apache.commons.dbcp2.BasicDataSource; ...

  7. Java 自定义FTP连接池

    转自:https://blog.csdn.net/eakom/article/details/79038590 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn ...

  8. java使用DBCP连接池创建工具类

    1.说明 java中有个扩展包 javax下面有个DataResource的接口  javax.sql.DataResource 该接口定义了连接池的方法规范 而DBCP框架有apache公司开发,他 ...

  9. Java并发——ThreadPoolExecutor线程池解析及Executor创建线程常见四种方式

    前言: 在刚学Java并发的时候基本上第一个demo都会写new Thread来创建线程.但是随着学的深入之后发现基本上都是使用线程池来直接获取线程.那么为什么会有这样的情况发生呢? new Thre ...

随机推荐

  1. 考勤机sql语句

    考勤机sql语句 SELECT checkinout.id as 序号 ,checkinout.pin as 打卡编号,userinfo.name 姓名, checkinout.checktime 签 ...

  2. icon-font 字体图标的引用

    1.font-class引用 font-class是unicode使用方式的一种变种,主要是解决unicode书写不直观,语意不明确的问题. 与unicode使用方式相比,具有如下特点: 兼容性良好, ...

  3. React - React Developer Tools开发者工具的安装与使用(Chrome调试插件)

    原文地址:http://www.cnplugins.com/zhuanti/how-to-use-react-tools.html 虽然我们曾经在React开发者工具的基础介绍里面有概括性的介绍过Re ...

  4. SpringBoot页面访问处理

    SpringBoot页面访问处理 1.介绍 Springboot推荐使用thymeleaf模板引擎搭载html页面实现jsp动态渲染效果,因此这里才会用该种方案进行. 2.集成步骤 引入thymele ...

  5. Python学习---重点模块之shelve

    简单示例 import shelve f = shelve.open(r'shelve.txt') f['info'] = {'name':'ftl', 'age':23, 'sex': 'male' ...

  6. 数据结构之排序技术:快速排序、归并排序、堆排序(C++版)

    快速排序 #include <iostream> using namespace std; void swap(int num[], int i, int j) { int temp = ...

  7. IIS7.5使用web.config设置伪静态的方法

    IIS 7和IIS 7.5及以后的版本估计都会使用web.config来实现伪静态规则,于是我们以前的伪静态文件必须更改.网上找了一圈,还没有发现比较全面的web.config伪静态规则,于是我们这里 ...

  8. php红包生成随机算法

    一.适用场景 红包总金额X,分配成Y个红包,每个红包随机金额. 二.生成算法 /** * 红包生成算法 * @param $money 总金额 * @param $number 红包数量 * @par ...

  9. 解决yii2.0里url重写引用js路径问题(@web/的用法)

    在实际项目中,为了seo优化,使用了伪静态,开启了url重写

  10. MapReduce过程详解(基于hadoop2.x架构)

    本文基于hadoop2.x架构详细描述了mapreduce的执行过程,包括partition,combiner,shuffle等组件以及yarn平台与mapreduce编程模型的关系. mapredu ...