• 欢迎访问 winrains 的个人网站!
  • 本网站主要从互联网整理和收集了与Java、网络安全、Linux等技术相关的文章,供学习和研究使用。如有侵权,请留言告知,谢谢!

手写数据库连接池

Java技术 winrains 来源:周鑫磊 6个月前 (03-31) 33次浏览

1 数据库连接池

1.1 数据库连接池原理

在内部对象池中,维护一定数量的数据库连接,并对外暴露数据库连接的获取和返回方法。

如外部使用者可通过getConnection方法获取数据库连接,使用完毕后再通过releaseConnection方法将连接返回,注意此时的连接并没有关闭,而是由连接池管理器回收,并为下一次使用做好准备。

1.2 线程池作用

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

2 常用数据库连接池

2.1 C3P0

C3P0是一个开放源代码的JDBC连接池,它在lib目录中与Hibernate一起发布,包括了实现jdbc3和jdbc2扩展规范说明的Connection 和Statement 池的DataSources 对象。

2.2 BoneCP

BoneCP 是一个开源的快速的 JDBC 连接池。BoneCP很小,只有四十几K(运行时需要log4j和Google Collections的支持,这二者加起来就不小了),而相比之下 C3P0 要六百多K。另外个人觉得 BoneCP 有个缺点是,JDBC驱动的加载是在连接池之外的,这样在一些应用服务器的配置上就不够灵活。当然,体积小并不是 BoneCP 优秀的原因,BoneCP 到底有什么突出的地方呢,请看看性能测试报告。池,Tomcat的数据源使用的就是DBCP。目前 DBCP 有两个版本分别是 1.3 和 1.4。1.3 版

2.3 DBCP

DBCP (Database Connection Pool)是一个依赖Jakarta commons-pool对象池机制的数据库连接本对应的是 JDK 1.4-1.5 和 JDBC 3,而1.4 版本对应 JDK 1.6 和 JDBC 4。因此在选择版本的时候要看看你用的是什么 JDK 版本了,功能上倒是没有什么区别。

2.4 Proxool

Proxool是一个Java SQL Driver驱动程序,提供了对你选择的其它类型的驱动程序的连接池封装。可以非常简单的移植到现存的代码中。完全可配置。快速,成熟,健壮。可以透明地为你现存的JDBC驱动程序增加连接池功能。

2.5 HikariCP

快速,简单,可靠。HikariCP是一个“零开销”生产就绪的JDBC连接池。库大约为130Kb

2.6 DBCP与C3P0区别

dbcp没有自动的去回收空闲连接的功能 c3p0有自动回收空闲连接功能 两者主要是对数据连接的处理方式不同!C3P0提供最大空闲时间,DBCP提供最大连接数。 前者当连接超过最大空闲连接时间时,当前连接就会被断掉。DBCP当连接数超过最大连接数时,所有连接都会被断

3 手写数据库连接池

3.1 手写数据库连接池步骤

  • 初始化线程池核心容器 空闲线程数、活动线程数
  1. 初始化线程,存放在空闲线程池中
  • 获取连接
  1. 判断存在线程数是否大于最大线程 如果大于最大线程数,则进行等待
  2. 判断空闲线程数是否大于0 如果空闲线程数<0,创建新的连接
  3. 如果空闲线程数>0,则获取当前空闲线程,存入在活动线程集合中
  • 释放连接
  1. 判断空闲线程数是否大于最大线程数
  2. 如果空闲线程数小于最大线程数,将该连接收回到 空闲 线程集合中
  3. 删除该连接对应的活动线程集合数据

3.2 读取外部配置信息

public class DbBean {
  /* 链接属性 */
  private String driverName = "com.mysql.jdbc.Driver";
  private String url = "jdbc:mysql://localhost:3306/test";
  private String userName = "root";
  private String password = "root";
  private String poolName = "thread01";// 连接池名字
  private int minConnections = 1; // 空闲池,最小连接数
  private int maxConnections = 10; // 空闲池,最大连接数
  private int initConnections = 5;// 初始化连接数
  private long connTimeOut = 1000;// 重复获得连接的频率
  private int maxActiveConnections = 100;// 最大允许的连接数,和数据库对应
  private long connectionTimeOut = 1000 * 60 * 20;// 连接超时时间,默认20分钟
  public String getDriverName() {
    return driverName;
  }
  public void setDriverName(String driverName) {
    this.driverName = driverName;
  }
  public String getUrl() {
    return url;
  }
  public void setUrl(String url) {
    this.url = url;
  }
  public String getUserName() {
    return userName;
  }
  public void setUserName(String userName) {
    this.userName = userName;
  }
  public String getPassword() {
    return password;
  }
  public void setPassword(String password) {
    this.password = password;
  }
  public String getPoolName() {
    return poolName;
  }
  public void setPoolName(String poolName) {
    this.poolName = poolName;
  }
  public int getMinConnections() {
    return minConnections;
  }
  public void setMinConnections(int minConnections) {
    this.minConnections = minConnections;
  }
  public int getMaxConnections() {
    return maxConnections;
  }
  public void setMaxConnections(int maxConnections) {
    this.maxConnections = maxConnections;
  }
  public int getInitConnections() {
    return initConnections;
  }
  public void setInitConnections(int initConnections) {
    this.initConnections = initConnections;
  }
  public long getConnTimeOut() {
    return connTimeOut;
  }
  public void setConnTimeOut(long connTimeOut) {
    this.connTimeOut = connTimeOut;
  }
  public int getMaxActiveConnections() {
    return maxActiveConnections;
  }
  public void setMaxActiveConnections(int maxActiveConnections) {
    this.maxActiveConnections = maxActiveConnections;
  }
  public long getConnectionTimeOut() {
    return connectionTimeOut;
  }
  public void setConnectionTimeOut(long connectionTimeOut) {
    this.connectionTimeOut = connectionTimeOut;
  }
}

3.3 读取外部配置信息

public class ConnectionPool implements IConnectionPool {
  // 空闲线程集合
  private List<Connection> freeConnection = new Vector<Connection>();
  // 活动线程集合
  private List<Connection> activeConnection = new Vector<Connection>();
  // 记录线程总数
  private static int connCount = 0;
  private DbBean dbBean;
  public ConnectionPool(DbBean dbBean) {
    this.dbBean = dbBean;
    init();
  }
  public void init() {
    try {
      for (int i = 0; i < dbBean.getInitConnections(); i++) {
        Connection newConnection = newConnection();
        if (newConnection != null) {
          // 添加到空闲线程中...
          freeConnection.add(newConnection);
        }
      }
    } catch (Exception e) {
    }
  }
  // 创建新的Connection
  private Connection newConnection() {
    try {
      if (dbBean == null) {
        return null;
      }
      Class.forName(dbBean.getDriverName());
      Connection connection = DriverManager.getConnection(dbBean.getUrl(), dbBean.getUserName(),
          dbBean.getPassword());
      connCount++;
      return connection;
    } catch (Exception e) {
      return null;
    }
  }
  public Connection getConnection() {
    // * ####1.判断活动线程数是否大于最大线程 如果大于最大线程数,则进行等待...<br>
    Connection connection = null;
    try {
      if (connCount < dbBean.getMaxActiveConnections()) {
        // 还有活动线程可以使用
        // * ####2.判断空闲线程数是否大于0 如果空闲线程数<0,创建新的连接<br>
        if (freeConnection.size() > 0) {
          connection = freeConnection.remove(0);// 等于freeConnection.get(0);freeConnection.remove(0);
        } else {
          // 创建新的连接
          connection = newConnection();
        }
        boolean available = isAvailable(connection);
        if (available) {
          activeConnection.add(connection);
        } else {
          connCount--;// i--操作
          connection = getConnection();// 递归调用getConnection方法
        }
      } else {
        // 大于最大线程数,进行等待,重新获取连接
        wait(dbBean.getConnTimeOut());
        connection = getConnection();// 递归调用getConnection方法
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    // * ####3.如果空闲线程数>0,则获取当前空闲线程,存入在活动线程集合中 <br>
    return connection;
  }
  // 判断连接是否可用
  public boolean isAvailable(Connection connection) {
    try {
      if (connection == null || connection.isClosed()) {
        return false;
      }
    } catch (Exception e) {
      // TODO: handle exception
    }
    return true;
  }
  public void releaseConnection(Connection connection) {
    try {
      if (connection == null) {
        return;
      }
      if (isAvailable(connection)) {
        // 判断空闲线程数是否大于最大线程数
        if (freeConnection.size() < dbBean.getMaxConnections()) {
          freeConnection.add(connection);
        } else {
          // 空闲线程数已经满了
          connection.close();
        }
        activeConnection.remove(connection);
        connCount--;
        notifyAll();
      }
    } catch (Exception e) {
    }
  }
}

3.4 测试运行

public class Test001 {
  public static void main(String[] args) {
    DBThread dBThread = new DBThread();
    for (int i = 1; i <= 3; i++) {
      Thread thread = new Thread(dBThread, "用户线程" + i);
      thread.start();
    }
  }
}
class DBThread implements Runnable {
  public void run() {
    for (int i = 0; i < 10; i++) {
      Connection connection = ConnectionPoolManager.getConnection();
      System.out.println(Thread.currentThread().getName() + ",connection:" + connection);
      ConnectionPoolManager.releaseConnection(connection);
    }
  }
}

作者:周鑫磊

来源:https://www.sparksys.top/archives/20


版权声明:文末如注明作者和来源,则表示本文系转载,版权为原作者所有 | 本文如有侵权,请及时联系,承诺在收到消息后第一时间删除 | 如转载本文,请注明原文链接。
喜欢 (1)