手写数据库连接池

基础介绍

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

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

手写连接池步骤

基础配置

创建一个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分钟
}

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

连接池类

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

import java.sql.Connection;

public interface ConnectionPool {
    //获取连接
    public Connection getConnection();
    //销毁连接
    public void releaseConnection(Connection connection);
}

实现该接口

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

活动连接池中存放的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;
    }
}
测试
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();
        }
    }
}
运行结果

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

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

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

更改的配置

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

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

public static final int maxActiveConnections = 10;// 最大允许的连接数
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值