实现一个简单的数据库连接池

实现思路:

1.连接池只能有一个,需要使用单例来构建连接池对象

这里采用的是双重检测锁构建单例对象,其中为了避免在初始化连接池对象时可能出现指令重排问题所造成的影响,给连接池对象添加了关键字volatile

/**
     * 连接池对象
     */
    private static volatile ConnectionPool instance;
    private ConnectionPool(){
        //防止反射破坏单例
        if(instance!=null){
            throw new RuntimeException("Object has been instanced!!!");
        }
         //初始化连接池
        init();
    }
    public static ConnectionPool getInstance(){
        //双重检测锁
        if(null==instance){
            synchronized (ConnectionPool.class){
                if(null==instance){
                    instance=new ConnectionPool();
                }
            }
        }
        return instance;
    }
2.构建连接池所需要的属性

这里的构建的属性都通过读取properties文件进行赋值,其中和连接数量有关的属性都设置一个默认值

/**
     * 数据库连接属性
     */
    private static String driverClass;
    private static String url;
    private static String username;
    private static String password;
    /**
     * 初始连接数量
     */
    private static int initCount=5;
    /**
     * 最小连接数量
     */
    private static int minCount=5;
    /**
     * 最大连接数量
     */
    private static int maxCount=20;
    /**
     * 已创建的连接数量
     */
    private static int createdCount;
    /**
     * 连接数增长步长
     */
    private static int increasingCount=2;
    /**
     * 存储连接的集合
     */
    LinkedList<Connection> conns=new LinkedList<>();
3.初始化连接池属性

采用在静态代码块内读取properties文件的方式给连接池参数赋值,其中对连接数量有关的属性都做了在未赋值的情况下的异常处理。

/**
     * 属性初始化
     */
    static{
        Properties properties=new Properties();
        InputStream in=ConnectionPool.class.getClassLoader().getResourceAsStream("db.properties");
        try {
            properties.load(in);
            driverClass=properties.getProperty("jdbc.driverClass");
            url=properties.getProperty("jdbc.url");
            username=properties.getProperty("jdbc.username");
            password=properties.getProperty("jdbc.password");
            //以下属性如果在properties文件中没有设置则使用默认值
            try{
                initCount=Integer.parseInt(properties.getProperty("jdbc.initCount"));
            }catch (Exception e){
                System.out.println("initCount使用默认值:"+initCount);
            }
            try {
                minCount=Integer.parseInt(properties.getProperty("jdbc.minCount"));
            }catch (Exception e){
                System.out.println("minCount使用默认值:"+minCount);
            }
            try {
                maxCount=Integer.parseInt(properties.getProperty("jdbc.maxCount"));
            }catch (Exception e){
                System.out.println("maxCount使用默认值:"+maxCount);
            }
            try {
                increasingCount=Integer.parseInt(properties.getProperty("jdbc.increasingCount"));
            }catch (Exception e){
                System.out.println("increasingCount使用默认值:"+increasingCount);
            }



        } catch (IOException e) {
            e.printStackTrace();
        }
    }
4.初始化连接池

构建init()方法,用来给向连接池中添加连接对象,在连接池对象创建的时候由构造方法进行调用。

 /**
     * 初始化连接池
     */
    private void init(){
        //循环给集合中添加初始化连接
        for(int i=0;i<initCount;i++){
            boolean flag=conns.add(createConnection());
            if(flag){
                createdCount++;
            }
        }
        System.out.println("初始化完成!!!");
        System.out.println("连接池可用连接数量:"+createdCount);
    }
5.获取池中连接

当池中有可用连接的时候直接获取。
没有可用连接时先判断创建的连接数是否已达到上限,未达到上限则调用自动增加连接数量的方法创建新的连接;
如果创建连接数量已达到上限,进行等待,等待之后通过return再次调用获取连接池的方法,直到获取到连接才会结束方法。

/**
     * 获取池中连接
     * @return
     */
    public Connection getConnection(){
        //判断池中是否还有连接
        if(conns.size()>0){
            return conns.removeFirst();
        }
        //如果没有空连接,则调用自动增长方法
        if(createdCount<maxCount){
            autoAdd();
            return getConnection();
        }
        //如果连接池连接数量达到上限,则等待连接归还
        System.out.println("连接池中连接已用尽,请等待连接归还");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return getConnection();
    }
6.自动增加连接数量

设置为同步方法,防止多个线程调用该方法出现的过多创建连接的问题,每添加一个连接都会与可创建最大连接数进行一次比较。

/**
     * 连接自动增长
     */
    private synchronized void autoAdd(){
        //增长步长默认为2
        if(createdCount==maxCount){
            throw new RuntimeException("连接池中连接已达最大数量,无法再次创建连接");
        }
        //临界时判断增长个数
        for(int i=0;i<increasingCount;i++){
            if(createdCount==maxCount){
                break;
            }
            conns.add(createConnection());
            createdCount++;
        }

    }
7.自动减少连接数量

这里以minCount作为最小空闲连接数,当目前的所创建的连接数大于最小空闲数且仍有可用连接时将该连接关闭。

 /**
     * 自动减少连接
     */
    private synchronized void autoReduce(){
        if(createdCount>minCount&&conns.size()>0){
            //关闭池中空闲连接
            try {
                conns.removeFirst().close();
                createdCount--;
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
8.归还连接

先将此连接添加会连接池的连接集合中,再调用减少连接数量的方法。

/**
     * 归还连接
     * @param conn
     */

    public void returnConnection(Connection conn){
        System.out.println("归还数据库连接");
        conns.add(conn);
        //归还之后,减少连接
        autoReduce();
    }
9.连接池的使用

创建工具类JDBCTool,构建静态成员变量获取连接池,构建关闭各种连接参数的方法,在获取连接和关闭连接时调用了连接池类的对应方法
JDBCTool:

package com.xxbb.connectionpool1;

import java.sql.*;

public class JDBCTool {
    private  static ConnectionPool connectionPool=ConnectionPool.getInstance();
    //获取连接
    public static Connection getConnection(){
        return connectionPool.getConnection();

    }
     //关闭连接
    public static void closeConnection(Connection conn){
        if(conn!=null){
            ConnectionPool.getInstance().returnConnection(conn);
        }
    }
    public static void closeAll(Connection conn, Statement stat, ResultSet rs){
        closeResultSet(rs);
        closeStatement(stat);
        closeConnection(conn);
    }
    public static void closeAll(Connection conn, PreparedStatement ps, ResultSet rs){
        closeResultSet(rs);
        closePreparedStatement(ps);
        closeConnection(conn);
    }
    public static void closeStatement(Statement stat){
        if(stat!=null){
            try {
                stat.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    public static void closePreparedStatement(PreparedStatement ps){
        if(ps!=null){
            try {
                ps.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    public static void closeResultSet(ResultSet rs){
        if(rs!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

个人小结

这次简单连接池的编写让我了解了一点连接池内部的运行原理,同时也对单例模式中的双重检测锁有了更进一步的认识,代码本身简单易写。在写完这个连接池之后又去学了下通过继承DataSource,使用动态代理实现数据库连接池的方法,实现的功能为获取连接和归还关闭连接,整体的逻辑还在归纳总结中。

完整代码

package com.xxbb.connectionpool1;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.Properties;

public class ConnectionPool {

    /**
     * 数据库连接属性
     */
    private static String driverClass;
    private static String url;
    private static String username;
    private static String password;
    /**
     * 初始连接数量
     */
    private static int initCount=5;
    /**
     * 最小连接数量
     */
    private static int minCount=5;
    /**
     * 最大连接数量
     */
    private static int maxCount=20;
    /**
     * 已创建的连接数量
     */
    private static int createdCount;
    /**
     * 连接数增长步长
     */
    private static int increasingCount=2;
    /**
     * 存储连接的集合
     */
    LinkedList<Connection> conns=new LinkedList<>();
    /**
     * 属性初始化
     */
    static{
        Properties properties=new Properties();
        InputStream in=ConnectionPool.class.getClassLoader().getResourceAsStream("db.properties");
        try {
            properties.load(in);
            driverClass=properties.getProperty("jdbc.driverClass");
            url=properties.getProperty("jdbc.url");
            username=properties.getProperty("jdbc.username");
            password=properties.getProperty("jdbc.password");
            //以下属性如果在properties文件中没有设置则使用默认值
            try{
                initCount=Integer.parseInt(properties.getProperty("jdbc.initCount"));
            }catch (Exception e){
                System.out.println("initCount使用默认值:"+initCount);
            }
            try {
                minCount=Integer.parseInt(properties.getProperty("jdbc.minCount"));
            }catch (Exception e){
                System.out.println("minCount使用默认值:"+minCount);
            }
            try {
                maxCount=Integer.parseInt(properties.getProperty("jdbc.maxCount"));
            }catch (Exception e){
                System.out.println("maxCount使用默认值:"+maxCount);
            }
            try {
                increasingCount=Integer.parseInt(properties.getProperty("jdbc.increasingCount"));
            }catch (Exception e){
                System.out.println("increasingCount使用默认值:"+increasingCount);
            }



        } catch (IOException e) {
            e.printStackTrace();
        }
    }



    /**
     * 连接池对象
     */
    private static volatile ConnectionPool instance;
    private ConnectionPool(){

        //防止反射破坏单例
        if(instance!=null){
            throw new RuntimeException("Object has been instanced!!!");
        }
        //初始化连接池
        init();
    }
    public static ConnectionPool getInstance(){
        //双重检测锁
        if(null==instance){
            synchronized (ConnectionPool.class){
                if(null==instance){
                    instance=new ConnectionPool();
                }
            }
        }
        return instance;
    }

    /**
     * 初始化连接池
     */
    private void init(){
        //循环给集合中添加初始化连接
        for(int i=0;i<initCount;i++){
            boolean flag=conns.add(createConnection());
            if(flag){
                createdCount++;
            }
        }
        System.out.println("初始化完成!!!");
        System.out.println("连接池可用连接数量:"+createdCount);
    }

    /**
     * 构建数据库连接对象
     * @return
     */
    private Connection createConnection(){
        try{
            Class.forName(driverClass);
            return DriverManager.getConnection(url,username,password);

        }catch (Exception e){
            throw new RuntimeException("数据库连接创建失败:"+e.getMessage());
        }
    }

    /**
     * 连接自动增长
     */
    private synchronized void autoAdd(){
        //增长步长默认为2
        if(createdCount==maxCount){
            throw new RuntimeException("连接池中连接已达最大数量,无法再次创建连接");
        }
        //临界时判断增长个数
        for(int i=0;i<increasingCount;i++){
            if(createdCount==maxCount){
                break;
            }
            conns.add(createConnection());
            createdCount++;
        }

    }

    /**
     * 获取池中连接
     * @return
     */
    public Connection getConnection(){
        //判断池中是否还有连接
        if(conns.size()>0){
            return conns.removeFirst();
        }
        //如果没有空连接,则调用自动增长方法
        if(createdCount<maxCount){
            autoAdd();
            return getConnection();
        }
        //如果连接池连接数量达到上限,则等待连接归还
        System.out.println("连接池中连接已用尽,请等待连接归还");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return getConnection();
    }

    /**
     * 自动减少连接
     */
    private synchronized void autoReduce(){
        if(createdCount>minCount&&conns.size()>0){
            //关闭池中空闲连接
            try {
                conns.removeFirst().close();
                createdCount--;
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 归还连接
     * @param conn
     */

    public void returnConnection(Connection conn){
        System.out.println("归还数据库连接");
        conns.add(conn);
        //归还之后,减少连接
        autoReduce();
    }
    /**
     * 返回可用连接数量
     * @return
     */
    public int getCreatedCount(){
        return createdCount;
    }

}


发布了24 篇原创文章 · 获赞 4 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_44804750/article/details/104872404