Loading [MathJax]/jax/input/TeX/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >自己手动写代码实现数据库连接池

自己手动写代码实现数据库连接池

作者头像
小勇DW3
发布于 2018-08-30 01:39:54
发布于 2018-08-30 01:39:54
1.1K00
代码可运行
举报
文章被收录于专栏:小勇DW3小勇DW3
运行总次数:0
代码可运行
概念

池:一个高级的集合体(集合存储元素 + 管理方式–>提高效率),是对外提供同一种类型对象的集合,如(线程池、数据库连接池)  特性:复用性(每条连接可重复使用),隔离性(每条连接一个时间内只能由一个线程获取)!!  连接池功能:  1.最大限度的满足并发复用,提高响应效率  2.大大的节省了服务器资源,(一定程度上)减少大量初始化问题

代码实现:
1.POOL Interface设计思想

一对外提供复用连接包装内<—[pool]—>一对内创建连接

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//抽取连接池架构接口
public interface MyPool {
//对外提供可复用连接包装内
PooledConnection getConnection();
//对内创建连接
void createConnections(int count);
}
2.PooledConnection

为自定义连接池包装类bean(原生的Connection没有复用的标志,若不close回收掉,则不能判断该connection是否在用情况)  成员变量:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//表示繁忙标志     复用的标志 线程安全
private boolean busy = false;
//真正的sql 连接connection(java.sql.Connection)
private Connection con;
//只是用来测试当前connectionName,便于观察
private String connName;

对外提供关闭方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 将该连接置为不可用,而不是真正关掉连接
public void close() {
    this.busy = false;
}

对外提供一个简单的测试方法,也就是获得了连接之后,就可以使用statement进行执行Sql语句;

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public ResultSet queryBySql(String sql) {
    Statement sttm = null;
    ResultSet rs = null;
    try {
        sttm = con.createStatement();
        rs = sttm.executeQuery(sql);
        //System.out.println("当前连接编号是:" + connName);
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return rs;
}

上面一个简单的PooledConnection连接包装类就完成了,主要功能就如之前所说的一样,将DriverManager获取的Connection进行包装使其可复用(连接不是用完就关掉),隔离性由一个简单的标志位属性busy决定(当busy为false时,表明该连接空闲可用,为true则表示该连接已被使用)

3.MyPoolImpl(重点关键实现类)为Pool的实现类

功能:  1.初始化数据连接driver  2.初始化连接池容量(扩容容量)  3.获取连接单个连接  成员变量

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private static String driver = null;
private static String url = null;
private static String user = null;
private static String password = null;
/**连接池中管道参数**/
private static int initCount = 5;
private static int stepSize = 10;
private static int poolMaxSize = 55;
private static int expandTime = 0;
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**线程安全集合,用来放(可复用)数据库连接管道(集合之前用Vector,但在测试的时候发现多线程并发出错了ConcurrentModificationException)这个还要仔细研究一下**/
//连接池
private static CopyOnWriteArrayList<PooledConnection> pooledCons = new CopyOnWriteArrayList<PooledConnection>();

几个重要实现方法  1.注册driver(根据配置文件properties文件修改初始化参数JdbcUtil操作)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private void init() {
    InputStream inStream = this.getClass().getClassLoader().getResourceAsStream("jdbc.properties");
    Properties properties = new Properties();
    try {
        properties.load(inStream);
    } catch (IOException e) {
        //若这里抛出异常则下面不运行
        e.printStackTrace();
    }
    this.driver = properties.getProperty("jdbc_driver");
    this.url = properties.getProperty("jdbc_url");
    this.user = properties.getProperty("jdbc_username");
    this.password = properties.getProperty("jdbc_password");
    if(Integer.valueOf(properties.getProperty("initCount")) > 0) {
        this.initCount = Integer.valueOf(properties.getProperty("initCount"));
    }else if(Integer.valueOf(properties.getProperty("stepSize")) > 0) {
        this.stepSize = Integer.valueOf(properties.getProperty("stepSize"));
    }else if(Integer.valueOf(properties.getProperty("poolMaxSize")) > 0) {
        this.poolMaxSize = Integer.valueOf(properties.getProperty("poolMaxSize"));
    }
    //准备创建DriverManager
    try {
        Driver dbDriver = (Driver) Class.forName(this.driver).newInstance();
        DriverManager.registerDriver(dbDriver);
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    //获取连接,用create方法获取
    /**DriverManager.getConnection(url, user, password);**/
    this.createConnections(initCount);
    }

2.初始化连接池容量(也就是实现Pool Interface的方法创建连接连接池)  所有的Console都是为了便于观察

代码语言:javascript
代码运行次数:0
运行
复制
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
public void createConnections(int count) {
    //this.expandTime++;
    //System.out.println("第"+expandTime+"次扩容,扩容量为:"+count);
    if((pooledConnections.size() + count) <= poolMaxSize) {
        for(int i = 0;i < count ;i++) {
            try {
                //获取连接放入线程安全的连接池中
                Connection conn = DriverManager.getConnection(url, user, password);
                PooledConnection pooledConnection = new PooledConnection(conn,false,String.valueOf(i));
                this.pooledConnections.add(pooledConnection);
                //System.out.println("初始化"+(i + 1) + "个连接");
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    System.out.println("当前连接池连接数量:"+ pooledConnections.size());
    System.out.println("最大连接池数量为:"+ this.poolMaxSize);
}
代码语言:javascript
代码运行次数:0
运行
复制

3.1 对外提供获取连接包装类(也是实现Interface的方法)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
public PooledConnection getConnection() {
    //spring思想要拿连接,先判断管道集合中是否有连接
    if(this.pooledConnections.size() == 0) {
        System.out.println("连接池没有连接!");
        //如果没有就手动再建一把连接池
        this.createConnections(initCount);
    }
    PooledConnection connection = getRealConnection();
    //如果还是没有拿到,说明全部线程都处于busy状态,得扩容
    while(connection == null) {
        this.createConnections(stepSize);
        connection = getRealConnection();
        try {//拿到连接等待一会,防止连接又被别的线程抢夺
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    return connection;
}
代码语言:javascript
代码运行次数:0
运行
复制

3.2 真正的获取连接包装类getRealConnection()

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//同步方法,真正的获取连接(连接包装内包括:connection和标志位busy)
private synchronized PooledConnection getRealConnection() {
    for(PooledConnection conn:pooledConnections) {
        //判断该连接是否已被占用
        if(!conn.isBusy()) {//false为可用(空闲),true为占用(繁忙)
            Connection connection = conn.getConnection();
            try {
                //判断该连接是否在设定时间连接通数据库(连接通为true)
                if(!connection.isValid(2000)) {
                    connection = DriverManager.getConnection(url, user, password);
                }
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            conn.setBusy(true);
            return conn;
        }
    }
    //如果连接池中的连接都被占用,则返回null,由调用函数处理(即扩容)
    return null;
}

以上连接池实现大致功能就完成了,主要包涵初始化注册,连接池扩容和获取连接方法

4.连接池管理类对象的封装PoolManager
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class PoolManager {
private PoolManager(){}
/**
 * 内部类单利模式 伪造类加载器加载原理:实现线程安全问题(类加载器加载内部类实例是互斥特性)
 */
private static class createPool {
    private static MyPoolImpl poolImpl = new MyPoolImpl();
}
public static MyPool getInstace() {
    return createPool.poolImpl;
}
}

特性:每个线程对类加载内部类实例时是互斥

测试

测试类Test.class  测试主方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//测试2000条线程 jdk1.8 内部类用lambda表达示
public static void main(String[] args) {
    for(int i =0; i < 2000; i++) {
        new Thread(() -> selectDate()).start();
    }
}

测试方法selectDate()

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
成员变量
public static MyPool myPool = PoolManager.getInstace();
//方法加了synchronized之后,连接池不扩容了???
public static void selectDate() {       
    PooledConnection connection = myPool.getConnection();
    String sql = "select * from t_area";
    ResultSet rs = connection.queryBySql(sql);
    try {
        while(rs.next()) {
            String name = rs.getString("name");
            Integer id = rs.getInt("id");
            //System.out.println("当前线程:"+ Thread.currentThread().getName() +",id:"+ id + ",name" + name);
        }
    } catch (SQLException e) {
        e.printStackTrace();
    }
    connection.close();
}

问题:  1.myPoolImpl类中连接池的集合问题用线程安全的Vector,在多次测试中出现Java ConcurrentModificationException 异常Java ConcurrentModificationException异常原因和解决方法 2.当在测试方法selectDate()上加了synchronized之后,连接池不扩容问题???,相关锁问题还不够了解  3.运行观测问题:扩容的连接数量远远大于了实际运行使用的数量(基本上就是0-10号连接在用)??? 

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-08-05 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
使用Vagrant将您的笔记本电脑刻录到虚拟机
本文介绍了如何使用 Vagrant 在本地开发环境中模拟多客户机环境,以便在开发过程中轻松测试应用程序在客户机环境中的表现。通过使用 Vagrant,可以在一个控制台中模拟多个客户机环境,方便开发人员测试和调试应用程序。同时,Vagrant 还可以与 Ansible 等配置管理工具集成,实现自动化客户机环境部署和配置。
Steve Wang
2018/01/05
1.3K0
k8s 系列教程-Vagrant
vagrant是开源的虚拟机技术,相对于 vmware 而言更轻量级,操作更简便移植性更强,如果我们需要学习k8s或者搭建一些集群的话建议使用 Virtualbox+Vagrant。Vagrant 是创建虚拟机的工具,Virtualbox 是vagrant 管理工具,而且这两个软件是开源的,不需要我去付费或者破解。掌握 Vagrant技术对我们后续学习k8s会有很大的帮助
六个核弹
2022/12/23
8260
使用Vagrant在你的电脑上构建独立的虚拟开发环境
Techeek
2018/01/08
1.3K0
『中级篇』docker网络(23)
PS:本机基本上docker的多虚拟机网络已经完成了,比较简单,毕竟是演示环境,可能有老铁说,为啥设置成了自动获得IP,而不是静态IP,毕竟是学习的环境,我也尝试改成过静态IP但是ping不通,其实我的思路就是先让计算机帮我设置一个自动获取的IP,我可以在他的基础上设置自己的静态ip,这样保证可以用,对于静态IP如何设置我也写一份静态IP的配置吧,跟上边差距不太大。 还有老铁说 我不太习惯用vagrant 我喜欢用docker-machine,这个都无所谓了,其实重点是吧环境搭建起来。
IT架构圈
2018/08/01
3290
『中级篇』docker网络(23)
『中级篇』docker网络(23)
PS:本机基本上docker的多虚拟机网络已经完成了,比较简单,毕竟是演示环境,可能有老铁说,为啥设置成了自动获得IP,而不是静态IP,毕竟是学习的环境,我也尝试改成过静态IP但是ping不通,其实我的思路就是先让计算机帮我设置一个自动获取的IP,我可以在他的基础上设置自己的静态ip,这样保证可以用,对于静态IP如何设置我也写一份静态IP的配置吧,跟上边差距不太大。
IT架构圈
2018/06/28
3580
『中级篇』docker网络(23)
vagrant学习笔记 - Vagrantfile
《vagrant学习笔记 - 入门》中的hello vagrant配置文件,只是最基本的配置,它使用缺省的box配置初始化了一个虚拟机。有时候,我希望对vm做更详尽的配置,比如配置一次创建一组vm,搭建一个mfs的测试环境,他需要一台服务器做mfsmaster,两台服务器做mfs chunk server,一台服务器做metalogger,还有一台服务器做mfs client进行测试。
pollyduan
2019/11/04
1.4K0
Vagrant入门
简单地说,Vagrant让我们可以通过代码的方式快速地、可重复地创建针对不同虚拟环境的虚拟机,包括Virtualbox、AWS、Docker等。它使得我们可以一次性地、自动创建多个环境相同的虚拟机,对于软件开发和测试尤其有用。本文我们将以Virtualbox为例,看看Vagrant的基本使用。
全栈程序员站长
2021/11/10
4950
Vagrant详细教程
  VirtualBox 是一个跨平台的虚拟化工具,支持多个操作系统,根据自己的情况选择对应的版本下载即可。
Se7eN_HOU
2022/05/07
1K0
Vagrant详细教程
『中级篇』docker之虚拟机创建vagrant技巧(番外篇)(81)
PS:这个很类似docker的镜像,需要什么找什么镜像,这个是找对应的虚拟机,我选择的virtualbox,等于别人在特定的系统版本下预装了你需要的软件。对于学习开发很好用。
IT架构圈
2018/12/10
3720
Vagrant入门
Vagrant是一个基于Ruby的工具,用于创建和部署虚拟化开发环境。它 使用Oracle的开源VirtualBox虚拟化系统,使用 Chef创建自动化虚拟环境。
用户2131907
2019/02/27
9340
DevOps工具介绍连载(10)——Vagrant
原文链接:https://blog.csdn.net/qianghaohao/article/details/80038096
顾翔
2020/02/20
1.2K0
实战篇:一行命令安装Linux系统,超详细的 Vagrant 上手指南
注意:如果是macOS可以通过homebrew直接安装,Windows可以通过下载安装包进行安装。
Lucifer三思而后行
2021/08/17
9890
实战篇:一行命令安装Linux系统,超详细的 Vagrant 上手指南
『中级篇』docker-swarm创建一个多节点集群(43)
docker Swarm是Docker官方提供的一款集群管理工具,其主要作用是把若干台Docker主机抽象为一个整体,并且通过一个入口统一管理这些Docker主机上的各种Docker资源。源码地址:h
IT架构圈
2018/08/16
2820
『中级篇』docker-swarm创建一个多节点集群(43)
vagrant + virtualbox搭建一个可移动的开发环境
vagrant+virtualbox 的出现,成功的解决了搭建开发环境耗时且不一致的问题
崔哥
2022/05/25
3720
Vagrant搭建集群环境
平时大家可能需要在多机集群中做一些部署测试,下面介绍如何通过vagrant配置一个测试用的集群环境 1. 安装vagrant和virtualbox 自行解决 2. 安装box文件 根据需要下载centos或者ubuntu的vbox镜像,并添加到系统中,例如: vagrant box add alpha xxx.box vagrant box add centos centos.box vagrant box add xxx.box 3. 创建目录并初始化vagrant vagrant init <!-- m
高木工
2019/05/06
1.1K0
笔记:win10上快速搭建容器环境,不需要VM
1.5.1 在Win10上准备centos7 和大家说明一下,我们的目的仅仅是要安装一个centos7,然后在centos7上安装docker 如果搞不定vagrant+virtualbox的方式,也可以直接使用VM搭建一个centos7 或者你可以直接使用一台云服务器,上面安装了centos7 毕竟我们的目的只是为了得到一个centos7的机器,所以不必花太多精力在这个问题上折腾 我上课用的环境是 【 win10 64位 VirtualBox-6.0.12-133076-Win
源码之路
2020/12/01
6120
使用vagrant+virtualbox搭建centos7
这里使用的是win7系统,可能会出现powershell过低的情况,所以需要先检查本机的powershell版本
用户1215919
2021/12/28
3640
使用vagrant搭建验证环境
这周的工作需要在一个独立的kubernetes环境调试功能,自然而然地想到在本机装个虚拟机搭建这个环境。不过有同事推荐我试一下vagrant,久闻Vagrant大名,之前也经常在一些开源项目中看到它,今天花了些时间了解下这个新东西。
jeremyxu
2019/05/06
9400
有了Vagrant,以后就可以不用VirtualBox图形化管理虚拟机了
Vagrant是一个跨平台的虚拟机管理工具,我们以 Deepin 20.2.3 为例,安装和使用 Vagrant。我们在这里所说的 Vagrant 包括 Vagrant 工具本身 和 虚拟引擎工具 VirtualBox。
极客开发者
2022/01/18
1.3K0
windows 安装vagrant reload 失败; No Virtualbox Guest Additions installation found.[通俗易懂]
问题描述:已经安装vagrant-vbguest 插件,且版本为0.30,使用的box 为centos7
全栈程序员站长
2022/07/19
5820
windows 安装vagrant reload 失败; No Virtualbox Guest Additions installation found.[通俗易懂]
推荐阅读
相关推荐
使用Vagrant将您的笔记本电脑刻录到虚拟机
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验