池化技术简介

在我们使用数据库的过程中,我们往往使用数据库连接池而不是直接使用数据库连接进行操作,这是因为每一个数据库连接的创建和销毁的代价是昂贵的,而池化技术则预先创建了资源,这些资源是可复用的,这样就保证了在多用户情况下只能使用指定数目的资源,避免了一个用户创建一个连接资源,造成程序运行开销过大。关于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. 【转】ArcGIS Server10.1安装常见问题及解决方案

    转载自:http://www.higis.cn/Tech/tech/tId/85/ 最近因为更换系统的原因,重新安装了ArcGISServer 10.1.过程中遇到了几个小问题,虽然都一一解决了,但也 ...

  2. Idea工具使用

    Idea Project与module的理解 1.基础环境的搭建 1.1.IDEA使用--字体.编码和基本设置 2.插件的安装 2.1.在IDEA中配置Gauge环境 2.2.IdeaVim的安装:: ...

  3. Android Studio 2.3.2 下载 - 百度网盘

    Android Studio是一个为Android平台开发程序的集成开发环境,其包含用于构建Android应用所需的所有工具. Android Studio 2.3.2为最新的稳定版(截止到2017年 ...

  4. Django 和 struts 对比

    转自:http://www.blogjava.net/shaofan/archive/2007/04/06/109007.html 假设:用两者写一个最小的WEB程序.过程可以参照:1.struts的 ...

  5. leetcode Ch1-search 2014

    1. Search Insert Position class Solution { public: int searchInsert(int A[], int n, int target) { ,r ...

  6. tcp.cc

    ns2-tcp-tcp.cc /* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */ /* * Copyri ...

  7. 打通 Spark 系统运行内幕机制循环流程

    本课主题 打通 Spark 系统运行内幕机制循环流程 引言 通过 DAGScheduelr 面向整个 Job,然后划分成不同的 Stage,Stage 是从后往前划分的,执行的时候是從前往后执行的,每 ...

  8. python UI自动化实战记录十:执行测试及测试报告

    使用简单的unittest.TextTestRunner. 思路: 1 在report目录下创建当日测试报告目录 20190113 2 创建测试报告文件 f = 时间戳.txt 3 加载测试集,运行测 ...

  9. Kali-linux识别活跃的主机

    尝试渗透测试之前,必须先识别在这个目标网络内活跃的主机.在一个目标网络内,最简单的方法将是执行ping命令.当然,它可能被一个主机拒绝,也可能被接收.本节将介绍使用Nmap工具识别活跃的主机. 网络映 ...

  10. VBA与宏

    VBA与宏 ====== 刚开始的内容听起来很枯燥,请大家不要分心,耐着性子看下去,兴趣总是慢慢积累的. ----------------------------------------------- ...