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

概念

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

代码实现:

1.POOL Interface设计思想

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

//抽取连接池架构接口
public interface MyPool {
//对外提供可复用连接包装内
PooledConnection getConnection();
//对内创建连接
void createConnections(int count);
}
2.PooledConnection

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

//表示繁忙标志     复用的标志 线程安全
private boolean busy = false;
//真正的sql 连接connection(java.sql.Connection)
private Connection con;
//只是用来测试当前connectionName,便于观察
private String connName;

对外提供关闭方法

// 将该连接置为不可用,而不是真正关掉连接
public void close() {
    this.busy = false;
}

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

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.获取连接单个连接  成员变量

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;
/**线程安全集合,用来放(可复用)数据库连接管道(集合之前用Vector,但在测试的时候发现多线程并发出错了ConcurrentModificationException)这个还要仔细研究一下**/
//连接池
private static CopyOnWriteArrayList<PooledConnection> pooledCons = new CopyOnWriteArrayList<PooledConnection>();

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

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都是为了便于观察

@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);
}

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

@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;
}

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

//同步方法,真正的获取连接(连接包装内包括: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
public class PoolManager {
private PoolManager(){}
/**
 * 内部类单利模式 伪造类加载器加载原理:实现线程安全问题(类加载器加载内部类实例是互斥特性)
 */
private static class createPool {
    private static MyPoolImpl poolImpl = new MyPoolImpl();
}
public static MyPool getInstace() {
    return createPool.poolImpl;
}
}

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

测试

测试类Test.class  测试主方法

//测试2000条线程 jdk1.8 内部类用lambda表达示
public static void main(String[] args) {
    for(int i =0; i < 2000; i++) {
        new Thread(() -> selectDate()).start();
    }
}

测试方法selectDate()

成员变量
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号连接在用)??? 

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏枕边书

搭建自己的PHP框架心得(二)

续言 对于本次更新,我想说: 本框架由本人挑时间完善,而我还不是PHP大神级的人物,所以框架漏洞难免,求大神们指出。 本框架的知识点应用都会写在博客里,大家有什...

26780
来自专栏吴伟祥

Jmockdata随机模拟 Java 数据插件

     Jmockdta是一款实现模拟JAVA类型或对象的实例化并随机初始化对象的数据的工具框架。

12320
来自专栏大内老A

ASP.NET Core中的依赖注入(2):依赖注入(DI)

IoC主要体现了这样一种设计思想:通过将一组通用流程的控制从应用转移到框架之中以实现对流程的复用,同时采用“好莱坞原则”是应用程序以被动的方式实现对流程的定制。...

23980
来自专栏技术博文

PHP编程风格规范

本规范由 EasyChen 借鉴 SINA网络应用开发部《C++开发规范》和互动技术部《PHP4开发规范》,以及phpDocument规范 整理出的开发规范。我...

41070
来自专栏java系列博客

Java面试通关要点汇总集基础篇之参考答案

23840
来自专栏眯眯眼猫头鹰的小树杈

猫头鹰的深夜翻译:理解java的classloader

Java ClassLoader是java运行系统中一个至关重要但是经常被忽略的组件。它负责在运行时寻找并加载类文件。创建自定义的ClassLoader可以彻底...

14640
来自专栏陈树义

简单笔记

1、类的表面类型和实际类型 实例对象有两个类型:表面类型(Apparent Type)和实际类型(ActualType),表面类型是声明时的类型,实际类型是对象...

27150
来自专栏我是攻城师

关于Java里面多线程同步的一些知识

对于任何Java开发者来说多线程和同步是一个非常重要的话题。比较好的掌握同步和线程安全相关的知识将使得我们则更加有优势,同时这些知识并不是非常容易就能熟练掌握的...

11730
来自专栏史上最简单的Spring Cloud教程

Openresty最佳案例 | 第2篇:Lua入门

什么是lua Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能...

90390
来自专栏好好学java的技术栈

并发基础篇(二):Thread类的API总结

12540

扫码关注云+社区

领取腾讯云代金券