当前位置:   article > 正文

手写数据库连接池

手写数据库连接池

基础介绍

数据库连接池负责分配、管理和释放数据库连接,它允许应程序重复使用一个现有的数据库连接,而不是再重新建立一个。

作用
  • 资源重用 由于数据库连接得到重用,避免了频繁创建、释放连接引起的大量性能开销。在减少系统消耗的基础上,增进了系统环境的平稳性(减少内存碎片以级数据库临时进程、线程的数量)
  • 更快的系统响应速度 数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于池内备用。此时连接池的初始化操作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而缩减了系统整体响应时间。
  • 新的资源分配手段 对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接的配置,实现数据库连接技术。
  • 统一的连接管理,避免数据库连接泄露 在较为完备的数据库连接池实现中,可根据预先的连接占用超时设定,强制收回被占用的连接,从而避免了常规数据库连接操作中可能出现的资源泄露

手写连接池步骤

基础配置

创建一个DbConfig类在其中进行数据库连接池的基础配置

public class DbConfig {
    public static final String driverName = "com.mysql.jdbc.Driver"; //jdbc驱动

    public static final String url = "jdbc:mysql://localhost:3306/"; //本地数据库接口

    public static final String userName = "root"; //账号

    public static final String password = "password"; //密码

    public static final int maxConnections = 10; // 空闲池,最大连接数

    public static final int initConnections = 5;// 初始化连接数

    public static final long connTimeOut = 1000;// 重复获得连接的频率

    public static final int maxActiveConnections = 100;// 最大允许的连接数,和数据库相匹配

    public static final long connectionTimeOut = 1000 * 60 * 20;// 连接超时时间,这里为20分钟
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

千万别忘了添加数据库驱动文件,如图,不然程序跑不起来
在这里插入图片描述

连接池类

创建一个连接池工具接口ConnectionPool

import java.sql.Connection;

public interface ConnectionPool {
    //获取连接
    public Connection getConnection();
    //销毁连接
    public void releaseConnection(Connection connection);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

实现该接口

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicInteger;

public class ConnectionPoolImpl implements ConnectionPool {
    // 空闲连接池
    private List<Connection> freeConnection = new Vector<Connection>();
    // 活动连接池
    private List<ConnectionInfo> activeConnection = new Vector<ConnectionInfo>();
    //当前连接数
    private AtomicInteger countConne = new AtomicInteger(0);
    //初始化
    public ConnectionPoolImpl() {
        init();
    }
    private void init() {
        //初始化连接放到空闲连接池中
        if (DbConfig.initConnections > 0) {
            for (int i = 0; i < DbConfig.initConnections; i++) {
                //加入连接到空闲连接池中
                AddOneConnectToFree();
            }
        }
    }
    private void check() {
        TimeCheck timeCheck = new TimeCheck();
        // 延迟一个重复获得连接的频率开始检查,每一个重复获得连接的频率检查一次
        new Timer().schedule(timeCheck,Long.valueOf(DbConfig.connTimeOut),Long.valueOf(DbConfig.connTimeOut));
    }

    class TimeCheck extends TimerTask {
        public void run() {
            System.out.println("例行检查...");
            for (int i = 0; i < activeConnection.size(); i++) {
                ConnectionInfo info = activeConnection.get(i);
                // 连接获取时间(加入到活动连接池的时间)
                long startTime = info.getUseStartTime();
                // 获取系统当前时间
                long currentTime = System.currentTimeMillis();
                try {
                    if ((currentTime - startTime) > Long.valueOf(DbConfig.connectionTimeOut)) {
                        Connection conn = info.getConn();
                        if (conn != null && !conn.isClosed()) {
                            // 关闭连接
                            conn.close();
                            // 从活动连接池中移出
                            activeConnection.remove(i);
                            // 总连接数变化
                            countConne.decrementAndGet();
                            System.out.println("发现有超时连接强行关闭," + conn + ",空闲连接数:" + freeConnection.size() + "," + "在使用连接数:" + activeConnection.size() + ",总的连接数:" + countConne.get());
                        }
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                } finally {
                }
            }
        }
    }

    // 向空闲连接池中加入连接
    private void AddOneConnectToFree(){
        Connection connection = newConnection();
        if (connection!=null){
            // 连接可用,向空闲连接池中加入连接
            freeConnection.add(connection);
        }else {
            // 连接不可用,递归尝试
            AddOneConnectToFree();
        }
    }

    // 新建连接
    private Connection newConnection(){
        try{
            Class.forName(DbConfig.driverName);
            Connection connection = DriverManager.getConnection(DbConfig.url,DbConfig.userName,DbConfig.password);
            System.out.println("创建一个新的连接:"+connection);
            return connection;
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
    // 获取连接
    public synchronized Connection getConnection() {
        try{
            Connection connection = null;
            // 判断是否大于最大连接数
            if (countConne.get() < DbConfig.maxActiveConnections){
                // 判断空闲连接池是否有对象
                if (freeConnection.size() > 0){
                    // 有对象时从空闲连接池中获取一个连接
                    connection = freeConnection.remove(0);
                }else {
                    // 没有对象时新建连接
                    connection = newConnection();
                }
                    // 对象可用
                if (connection != null && !connection.isClosed()){
                    // 将连接对象加入到活动连接池中
                    activeConnection.add(new ConnectionInfo(connection,System.currentTimeMillis()));
                    // 总连接数改变
                    countConne.getAndIncrement();
                    System.out.println(Thread.currentThread().getName() + ",获取并使用连接:" + connection + ",空闲连接数:" + freeConnection.size() + "," + "活动连接数:" + activeConnection.size() + ",总的连接数:" + countConne.get());
                }else {
                    //如果对象不可用,递归重试
                    connection = getConnection();
                }

            }else {
                System.out.println(Thread.currentThread().getName() + ",连接池最大连接数为:" + DbConfig.maxConnections + "已经满了,需要等待...");
                //当达到最大连接时,等待后重试
                wait(DbConfig.connTimeOut);
                connection = getConnection();
            }
            return connection;
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
    //释放连接
    public synchronized void releaseConnection(Connection connection) {
        try{
            if (connection != null && !connection.isClosed()){
                // 从活动连接池中移出
                for (int i = 0; i < activeConnection.size(); i++) {
                    if (activeConnection.get(i).getConn() == connection) {
                        activeConnection.remove(i);
                    }
                }
                //判断空闲容器是否达到最大连接
                if (freeConnection.size() < DbConfig.maxConnections){
                    //小于最大连接,则添加到空闲容器中
                    freeConnection.add(connection);
                }else {
                    //大于最大连接直接关闭
                    connection.close();
                }
                // 总连接数改变
                countConne.getAndDecrement();
            }
            System.out.println("回收了一个连接:" + connection + ",空闲连接数为:" + freeConnection.size() + ",活动连接数为:" + activeConnection.size());
            // 唤醒其它线程
            notifyAll();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156

活动连接池中存放的ConnectionInfo类代码

import java.sql.Connection;

public class ConnectionInfo {
    private Connection conn;
    private long useStartTime; //开始使用时间
    public Connection getConn() {
        return conn;
    }

    public void setConn(Connection conn) {
        this.conn = conn;
    }
    public long getUseStartTime() {
        return useStartTime;
    }

    public void setUseStartTime(long useStartTime) {
        this.useStartTime = useStartTime;
    }
    public ConnectionInfo(Connection conn, long useStartTime) {
        super();
        this.conn = conn;
        this.useStartTime = useStartTime;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
测试
import java.sql.Connection;

public class Test {
    public static void main(String[] args) {
        // 创建连接池
        ConnectionPool connectionPool = new ConnectionPoolImpl();
        // 利用线程模拟获取连接
        for (int i = 0; i <= 10000; i++) {
            new Thread(()->{
                for (int j=0;j<6;j++){
                    Connection connection = connectionPool.getConnection();
                    connectionPool.releaseConnection(connection);
                }
            }).start();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
运行结果

开始
在这里插入图片描述
结束
在这里插入图片描述

为了呈现效果,我将连接池的连接数限制的更小。出现接池已满的现象
在这里插入图片描述

由于模拟的线程释放连接十分迅速,所以看不到超时的现象。

更改的配置

public static final int maxConnections = 5; // 空闲池,最大连接数

public static final int initConnections = 3;// 初始化连接数

public static final int maxActiveConnections = 10;// 最大允许的连接数
  • 1
  • 2
  • 3
  • 4
  • 5
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/877550
推荐阅读
相关标签
  

闽ICP备14008679号