User-Profile-Image
hankin
  • 5
  • Java
  • Kotlin
  • Spring
  • Web
  • SQL
  • MegaData
  • More
  • Experience
  • Enamiĝu al vi
  • 分类
    • Zuul
    • Zookeeper
    • XML
    • WebSocket
    • Web Notes
    • Web
    • Vue
    • Thymeleaf
    • SQL Server
    • SQL Notes
    • SQL
    • SpringSecurity
    • SpringMVC
    • SpringJPA
    • SpringCloud
    • SpringBoot
    • Spring Notes
    • Spring
    • Servlet
    • Ribbon
    • Redis
    • RabbitMQ
    • Python
    • PostgreSQL
    • OAuth2
    • NOSQL
    • Netty
    • MySQL
    • MyBatis
    • More
    • MinIO
    • MegaData
    • Maven
    • LoadBalancer
    • Kotlin Notes
    • Kotlin
    • Kafka
    • jQuery
    • JavaScript
    • Java Notes
    • Java
    • Hystrix
    • Git
    • Gateway
    • Freemarker
    • Feign
    • Eureka
    • ElasticSearch
    • Docker
    • Consul
    • Ajax
    • ActiveMQ
  • 页面
    • 归档
    • 摘要
    • 杂图
    • 问题随笔
  • 友链
    • Spring Cloud Alibaba
    • Spring Cloud Alibaba - 指南
    • Spring Cloud
    • Nacos
    • Docker
    • ElasticSearch
    • Kotlin中文版
    • Kotlin易百
    • KotlinWeb3
    • KotlinNhooo
    • 前端开源搜索
    • Ktorm ORM
    • Ktorm-KSP
    • Ebean ORM
    • Maven
    • 江南一点雨
    • 江南国际站
    • 设计模式
    • 熊猫大佬
    • java学习
    • kotlin函数查询
    • Istio 服务网格
    • istio
    • Ktor 异步 Web 框架
    • PostGis
    • kuangstudy
    • 源码地图
    • it教程吧
    • Arthas-JVM调优
    • Electron
    • bugstack虫洞栈
    • github大佬宝典
    • Sa-Token
    • 前端技术胖
    • bennyhuo-Kt大佬
    • Rickiyang博客
    • 李大辉大佬博客
    • KOIN
    • SQLDelight
    • Exposed-Kt-ORM
    • Javalin—Web 框架
    • http4k—HTTP包
    • 爱威尔大佬
    • 小土豆
    • 小胖哥安全框架
    • 负雪明烛刷题
    • Kotlin-FP-Arrow
    • Lua参考手册
    • 美团文章
    • Java 全栈知识体系
    • 尼恩架构师学习
    • 现代 JavaScript 教程
    • GO相关文档
    • Go学习导航
    • GoCN社区
    • GO极客兔兔-案例
    • 讯飞星火GPT
    • Hollis博客
    • PostgreSQL德哥
    • 优质博客推荐
    • 半兽人大佬
    • 系列教程
    • PostgreSQL文章
    • 云原生资料库
    • 并发博客大佬
Help?

Please contact us on our email for need any support

Support
    首页   ›   Java   ›   正文
Java

Java—数据库连接池

2022-07-17 18:26:20
539  0 1
参考目录 隐藏
1) 连接池概念
2) 手写连接池
3) 测试

阅读完需:约 11 分钟

连接池概念

数据库连接池是在程序初始化时创建一定数量的数据库连接对象并将其保存在一块内存区中,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个;释放空闲时间超过最大空闲时间的数据库连接以避免因为没有释放数据库连接而引起的数据库连接遗漏。

数据库连接池技术带来的优势:

1. 资源重用

由于数据库连接得到重用,避免了频繁创建、释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增进了系统运行环境的平稳性(减少内存碎片以及数据库临时进程/线程的数量)。

2. 更快的系统响应速度

数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而缩减了系统整体响应时间。

3. 新的资源分配手段

对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接的配置,实现数据库连接池技术,几年钱也许还是个新鲜话题,对于目前的业务系统而言,如果设计中还没有考虑到连接池的应用,那么…….快在设计文档中加上这部分的内容吧。某一应用最大可用数据库连接数的限制,避免某一应用独占所有数据库资源。

4. 统一的连接管理,避免数据库连接泄漏

在较为完备的数据库连接池实现中,可根据预先的连接占用超时设定,强制收回被占用连接。从而避免了常规数据库连接操作中可能出现的资源泄漏。

手写连接池

其实所谓的连接池就是一个线程安全的队列或者是一个集合,需要我们在初始化的时候去创建很多的连接存储起来,在需要使用的时候去获取,结束时释放。

首先创建一个连接池的属性信息类:

DbProperties

/**
 * 数据库连接池属性信息
 * @author xujiahui
 */
@Data
public class DbProperties {

    /* 链接属性 */
    private String driverName;

    private String url;

    private String userName;

    private String passWord;

    private String poolName;

    /**
     * 空闲池,最小连接数
     */
    private int minFreeConnections;

    /**
     * 空闲池,最大连接数
     */
    private int maxFreeConnections;

    /**
     * 初始连接数
     */
    private int initFreeConnections;

    /**
     * 重试获得连接的频率  毫秒
     */
    private long retryConnectionTimeOut;

    /**
     * 最大允许的连接数
     */
    private int maxConnections;

    /**
     * 连接超时时间
     */
    private long connectionTimeOut;

}

这里包含了连接池的数据信息与连接属性

其中数据库的信息写成配置文件pool.properties需要读取,文件位置为resoucers资源目录下

##驱动名称
driverName = org.postgresql.Driver
##数据库连接地址
url = jdbc:postgresql://82.157.173.xx:5432/smartpark?currentSchema=useroauth&useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8
userName = postgres
passWord = pg123456
##连接池名字
poolName = Pool
##空闲池,最小连接数
minFreeConnections = 1
##初始化空闲池连接数
initFreeConnections = 3
##空闲池,最大连接数
maxFreeConnections = 5
##最大允许的连接数,一般小于数据库总连接数
maxConnections = 15
##重试获得连接的频率  一秒
retryConnectionTimeOut = 1000
##连接超时时间,默认20分钟  1000 * 60 * 20
connectionTimeOut = 1200000

创建一个接口,接口上描述连接池的基本函数

DbPoolService

/**
 * @author xujiahui
 * @Description:数据库连接池
 */
public interface DbPoolService {

    /**
     * @Description:判断连接是否可用,可用返回true
     */
    public boolean isAvailable(Connection connection);

    /**
     * @Description:使用重复利用机制获取连接
     */
    public Connection getConnection();

    /**
     * @Description:使用可回收机制释放连接
     */
    public void releaseConnection(Connection connection);
}

创建一个类实现接口

DbPoolServiceImpl

/**
 * @Description:数据库连接池实现
 * @author xjh
 */
public class DbPoolServiceImpl implements DbPoolService {

    /**
     * 存放空闲连接的容器,除了可以使用并发队列,也可以使用线程安全的集合Vector
     */
    private BlockingQueue<Connection> freeConnection = null;
    /**
     * 存放活动连接的容器,除了可以使用并发队列,也可以使用线程安全的集合Vector
     */
    private BlockingQueue<Connection> activeConnection = null;

    /**
     * 存放映射的属性配置文件
     */
    private DbProperties dDbProperties;


    public DbPoolServiceImpl(DbProperties dDbProperties) throws Exception {
        // 获取配置文件信息
        this.dDbProperties = dDbProperties;
        freeConnection =  new LinkedBlockingQueue<>(dDbProperties.getMaxFreeConnections());
        activeConnection = new LinkedBlockingQueue<>(dDbProperties.getMaxConnections());
        init();
    }

    /**
     * @Description:初始化空闲线程池
     */
    private void init() throws Exception {
        System.out.println("初始化线程池开始,线程池配置属性:"+dDbProperties);
        if (dDbProperties == null) {
            throw new  Exception("连接池配置属性对象不能为空");
        }
        //获取连接池配置文件中初始化连接数
        for (int i = 0; i < dDbProperties.getInitFreeConnections(); i++) {
            //创建Connection连接
            Connection newConnection = newConnection();
            if (newConnection != null) {
                //将创建的新连接放入到空闲池中
                freeConnection.add(newConnection);
            }
        }
        System.out.println("初始化线程池结束,初始化线程数:"+dDbProperties.getInitFreeConnections());
    }

    /**
     * @Description: 新连接
     */
    private synchronized Connection newConnection() {
        try {
            Class.forName(dDbProperties.getDriverName());
            return DriverManager.getConnection(dDbProperties.getUrl(), dDbProperties.getUserName(),dDbProperties.getPassWord());
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * @Description:判断连接是否可用,可用返回true
     */
    @Override
    public boolean isAvailable(Connection connection) {
        try {
            if (connection == null || connection.isClosed()) {
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;

    }

    /**
     * @Description:使用重复利用机制获取连接:如果总连接未超过最大连接,则从空闲连接池获取连接或者创建一个新的连接,如果超过最大连接,则等待一段时间以后,继续获取连接
     */
    @Override
    public synchronized Connection getConnection() {
        Connection connection = null;
        //空闲连接和活动连接的总数加起来 小于 最大配置连接
        System.out.println("当前空闲连接总数:"+freeConnection.size()+" 当前活动连接总数"+activeConnection.size()+", 配置最大连接数:"+ dDbProperties.getMaxConnections());
        if (freeConnection.size()+activeConnection.size() < dDbProperties.getMaxConnections()) {
            //空闲连接池,是否还有还有连接,有就取出来,没有就创建一个新的。
            if (freeConnection.size() > 0) {
                connection = freeConnection.poll();
                System.out.println("从空闲线程池取出线程:"+connection+"当前空闲线程总数:"+freeConnection.size());
            } else {
                connection = newConnection();
                System.out.println("空闲连接池没有连接,创建连接"+connection);
            }
            //拿到的连接可用,就添加活动连接池,否则就递归继续找下一个
            boolean available = isAvailable(connection);
            if (available) {
                activeConnection.add(connection);
            } else {
                connection = getConnection();
            }

        } else {
            System.out.println("当前连接数已达到最大连接数,等待"+dDbProperties.getRetryConnectionTimeOut()+"ms以后再试");
            try {
                wait(dDbProperties.getRetryConnectionTimeOut());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            connection = getConnection();
        }
        return connection;

    }

    /**
     * @Description:使用可回收机制释放连接:如果连接可用,并且空闲连接池没有满,则把连接归还到空闲连接池,否则关闭连接
     */
    @Override
    public synchronized void releaseConnection(Connection connection) {
        try {
            if (isAvailable(connection) && freeConnection.size() < dDbProperties.getMaxFreeConnections()) {
                freeConnection.add(connection);
                System.out.println("空闲线程池未满,归还连接"+connection);

            } else {
                connection.close();
                System.out.println("空闲线程池已满,关闭连接"+connection);
            }
            activeConnection.remove(connection);
            notifyAll();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

创建一个类来管理连接池

DbPoolManager

/**
 * @Description:数据库连接池管理
 */
public class DbPoolManager {

    private static String sourcePath = "pool";

    /**
     * 数据库连接池配置属性
     */
    private static DbProperties properties = null;

    /**
     * 数据库连接池接口
     */
    private static DbPoolService connectionPool = null;


    /**
     * 双重检查机制静态加载连接池
     */
    static {
        try {
            if (properties == null) {
                synchronized (DbPoolManager.class) {
                    if (properties == null) {
                        properites2Object();
                        connectionPool = new DbPoolServiceImpl(properties);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * @Description:数据库连接池database配置文件映射到java对象
     */
    private static void properites2Object() throws NoSuchFieldException, IllegalAccessException {
        properties = new DbProperties();
        ResourceBundle resourceBundle = ResourceBundle.getBundle(sourcePath);
        //获取资源文件中所有的key
        Enumeration<String> keys = resourceBundle.getKeys();
        while (keys.hasMoreElements()) {
            String key = (String) keys.nextElement();
            //反射获取类中的属性字段
            Field field = DbProperties.class.getDeclaredField(key);
            //属性字段的类型
            Type genericType = field.getGenericType();
            //属性设置可访问 --- 当isAccessible()的结果是false时不允许通过反射访问该字段 --- 让我们在用反射时访问私有变量
            field.setAccessible(true);
            //根据key读取对应的value值
            String value = resourceBundle.getString(key);
            if ("int".equals(genericType.getTypeName())) {
                //反射给属性赋值
                field.set(properties, Integer.parseInt(value));
            } else if ("long".equals(genericType.getTypeName())) {
                field.set(properties, Long.parseLong(value));
            } else if ("java.lang.String".equals(genericType.getTypeName())) {
                field.set(properties, value);
            }
        }
    }

    /**
     * @Description:获取连接
     */
    public static Connection getConnection() {
        return connectionPool.getConnection();
    }

    /**
     * @Description:释放连接
     */
    public static void releaseConnection(Connection connection) {
        connectionPool.releaseConnection(connection);
    }
}

测试

/**
 * 连接池测试
 * @author xujiahui
 */
public class Test {

    public static void main(String[] args) {
        ThreadConnection threadConnection = new ThreadConnection();
        for (int i = 0; i < 20; i++) {
            Thread thread = new Thread(threadConnection, "线程:" + i);
            thread.start();
        }
    }
}

class ThreadConnection implements Runnable {

    public void run() {
        for (int i = 0; i < 20; i++) {
            Connection connection = DbPoolManager.getConnection();
            System.out.println(Thread.currentThread().getName() + ",connection:" + connection);
            Statement statement;
            try {
                statement = connection.createStatement();
                String selectsql = "select * from role";
                statement.execute(selectsql);
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            DbPoolManager.releaseConnection(connection);
        }
    }
}

结果

初始化线程池结束,初始化线程数:3
当前空闲连接总数:3 当前活动连接总数0, 配置最大连接数:15
从空闲线程池取出线程:org.postgresql.jdbc.PgConnection@1d96ee45当前空闲线程总数:2
当前空闲连接总数:2 当前活动连接总数1, 配置最大连接数:15
从空闲线程池取出线程:org.postgresql.jdbc.PgConnection@5746ac53当前空闲线程总数:1
线程:17,connection:org.postgresql.jdbc.PgConnection@5746ac53
线程:7,connection:org.postgresql.jdbc.PgConnection@1d96ee45
当前空闲连接总数:1 当前活动连接总数2, 配置最大连接数:15
从空闲线程池取出线程:org.postgresql.jdbc.PgConnection@288130de当前空闲线程总数:0
线程:15,connection:org.postgresql.jdbc.PgConnection@288130de
当前空闲连接总数:0 当前活动连接总数3, 配置最大连接数:15
空闲连接池没有连接,创建连接org.postgresql.jdbc.PgConnection@49dd4eca
线程:6,connection:org.postgresql.jdbc.PgConnection@49dd4eca
当前空闲连接总数:0 当前活动连接总数4, 配置最大连接数:15
空闲连接池没有连接,创建连接org.postgresql.jdbc.PgConnection@2cb32476
线程:19,connection:org.postgresql.jdbc.PgConnection@2cb32476
空闲线程池未满,归还连接org.postgresql.jdbc.PgConnection@49dd4eca
当前空闲连接总数:1 当前活动连接总数4, 配置最大连接数:15
从空闲线程池取出线程:org.postgresql.jdbc.PgConnection@49dd4eca当前空闲线程总数:0
线程:6,connection:org.postgresql.jdbc.PgConnection@49dd4eca
空闲线程池未满,归还连接org.postgresql.jdbc.PgConnection@288130de
当前空闲连接总数:1 当前活动连接总数4, 配置最大连接数:15
从空闲线程池取出线程:org.postgresql.jdbc.PgConnection@288130de当前空闲线程总数:0
线程:15,connection:org.postgresql.jdbc.PgConnection@288130de
空闲线程池未满,归还连接org.postgresql.jdbc.PgConnection@5746ac53
…………………………………………………………

如本文“对您有用”,欢迎随意打赏作者,让我们坚持创作!

1 打赏
Enamiĝu al vi
不要为明天忧虑.因为明天自有明天的忧虑.一天的难处一天当就够了。
543文章 68评论 294点赞 593486浏览

随机文章
Java—并发编程(八)线程池– 概览
3年前
SpringMVC笔记12—数据回显基本用法
5年前
Spring Boot—整合 Thymeleaf 页面模板
5年前
Spring笔记8—其他配置
5年前
Ajax状态值与状态码区别
5年前
博客统计
  • 日志总数:543 篇
  • 评论数目:68 条
  • 建站日期:2020-03-06
  • 运行天数:1927 天
  • 标签总数:23 个
  • 最后更新:2024-12-20
Copyright © 2025 网站备案号: 浙ICP备20017730号 身体没有灵魂是死的,信心没有行为也是死的。
主页
页面
  • 归档
  • 摘要
  • 杂图
  • 问题随笔
博主
Enamiĝu al vi
Enamiĝu al vi 管理员
To be, or not to be
543 文章 68 评论 593486 浏览
测试
测试
看板娘
赞赏作者

请通过微信、支付宝 APP 扫一扫

感谢您对作者的支持!

 支付宝 微信支付