Java 模拟数据库连接池的实现代码

 更新时间:2021年02月25日 08:31:58   作者:低吟不作语  
这篇文章主要介绍了Java 模拟数据库连接池的实现,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

前面学习过等待 - 通知机制,现在我们在其基础上添加一个超时机制,模拟从连接池中获取、使用和释放连接的过程。客户端获取连接的过程被设定为等待超时模式,即如果在 1000 毫秒内无法获取到可用连接,将会返回给客户端一个 null。设定连接池的大小为 10 个,然后通过调节客户端的线程数来模拟无法获取连接的场景

由于 java.sql.Connection 只是一个接口,最终实现是由数据库驱动提供方来实现,考虑到本例只是演示,我们通过动态代理构造一个 Connection,该 Connection 的代理仅仅是在调用 commit() 方法时休眠 100 毫秒

public class ConnectionDriver {

  static class ConnectionHandler implements InvocationHandler {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      if ("commit".equals(method.getName())) {
        TimeUnit.MICROSECONDS.sleep(100);
      }
      return null;
    }
  }

  /**
   * 创建一个 Connection 的代理,在 commit 时休眠 100 毫秒
   */
  public static Connection createConnection() {
    return (Connection) Proxy.newProxyInstance(ConnectionDriver.class.getClassLoader(),
        new Class<?>[]{Connection.class}, new ConnectionHandler());
  }
}

接下来是线程池的实现。本例通过一个双向队列来维护连接,调用方需要先调用 fetchConnection(long) 方法来指定在多少毫秒内超时获取连接,当连接使用完成后,需要调用 releaseConnection(Connection) 方法将连接放回线程池

public class ConnectionPool {

  private final LinkedList<Connection> pool = new LinkedList<>();

  public ConnectionPool(int initialSize) {
    // 初始化连接的最大上限
    if (initialSize > 0) {
      for (int i = 0; i < initialSize; i++) {
        pool.addLast(ConnectionDriver.createConnection());
      }
    }
  }

  public void releaseConnection(Connection connection) {
    if (connection != null) {
      synchronized (pool) {
        /* 连接释放后需要进行通知
         * 这样其他消费者就能知道连接池已经归还了一个连接
         */
        pool.addLast(connection);
        pool.notifyAll();
      }
    }
  }

  /**
   * 在给定毫秒时间内获取连接
   */
  public Connection fetchConnection(long mills) throws InterruptedException {
    synchronized (pool) {
      // 完全超时
      if (mills < 0) {
        while (pool.isEmpty()) {
          pool.wait();
        }
        return pool.removeFirst();
      } else {
        long future = System.currentTimeMillis() + mills;
        long remaining = mills;
        while (pool.isEmpty() && remaining > 0) {
          pool.wait(remaining);
          remaining = future - System.currentTimeMillis();
        }
        Connection result = null;
        if (!pool.isEmpty()) {
          result = pool.removeFirst();
        }
        return result;
      }
    }
  }
}

最后编写一个用于模拟客户端获取连接的示例,该示例将模拟多个线程同时从连接池获取连接,并记录总尝试获取数、获取成功数和获取失败数

public class ConnectionPoolTest {

  static ConnectionPool pool = new ConnectionPool(10);
  static CountDownLatch start = new CountDownLatch(1);
  static CountDownLatch end;

  public static void main(String[] args) throws InterruptedException {
    // 线程数量
    int threadCount = 200;
    end = new CountDownLatch(threadCount);
    int count = 20;
    AtomicInteger got = new AtomicInteger();
    AtomicInteger notGot = new AtomicInteger();
    for (int i = 0; i < threadCount; i++) {
      Thread thread = new Thread(new ConnectionRunner(count, got, notGot), "ConnectionRunnerThread");
      thread.start();
    }
    start.countDown();
    end.await();
    System.out.println("total invoke : " + (threadCount * count));
    System.out.println("got connection : " + got);
    System.out.println("not got connection : " + notGot);
  }

  static class ConnectionRunner implements Runnable {

    int count;
    AtomicInteger got;
    AtomicInteger notGot;

    public ConnectionRunner(int count, AtomicInteger got, AtomicInteger notGot) {
      this.count = count;
      this.got = got;
      this.notGot = notGot;
    }

    @Override
    public void run() {
      try {
        start.await();
      } catch (Exception e) {
        e.printStackTrace();
      }
      while (count > 0) {
        try {
          // 从线程池中获取连接,如果 1000ms 内无法获取到,将返回 null
          // 分别统计获取连接的数量 got 和未获取到的数量 notGot
          Connection connection = pool.fetchConnection(1000);
          if (connection != null) {
            try {
              connection.createStatement();
              connection.commit();
            } finally {
              pool.releaseConnection(connection);
              got.incrementAndGet();
            }
          } else {
            notGot.incrementAndGet();
          }
        } catch (Exception e) {
          e.printStackTrace();
        } finally {
          count--;
        }
      }
      end.countDown();
    }
  }
}

笔者设置线程数量为 200 时,得出结果如下

当设置为 500 时,得出结果如下,当然具体结果根据机器性能而异

可见,随着客户端线程数的增加,客户端出现超时无法获取连接的比率不断升高。这种等待超时模式能保证程序出问题时,线程不会一直运行,而是按时返回,并告知客户端获取连接出现问题。数据库连接池的实际也可以应用到其他资源获取的场景,针对昂贵资源的获取都应该加以限制

到此这篇关于Java 模拟数据库连接池的实现代码的文章就介绍到这了,更多相关Java 数据库连接池内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java并发编程线程间通讯实现过程详解

    Java并发编程线程间通讯实现过程详解

    这篇文章主要介绍了Java并发编程线程间通讯实现过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-05-05
  • 深入探究Java线程与进程有哪些区别

    深入探究Java线程与进程有哪些区别

    这篇文章主要介绍了Java并发编程之线程创建,进程是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,线程则是一个实体,一个进程中至少有一个线程,下文更多相关内容需要的小伙伴可以参考一下
    2022-04-04
  • 详解Spring Boot 项目中的 parent

    详解Spring Boot 项目中的 parent

    这篇文章主要介绍了Spring Boot中parent作用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • 解决springboot上传图片后无法立即访问需重启的问题

    解决springboot上传图片后无法立即访问需重启的问题

    这篇文章主要介绍了解决springboot上传图片后无法立即访问需重启的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-12-12
  • MySQL MyBatis 默认插入当前时间方式

    MySQL MyBatis 默认插入当前时间方式

    这篇文章主要介绍了MySQL MyBatis 默认插入当前时间方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-10-10
  • Spring Cloud Stream实现数据流处理

    Spring Cloud Stream实现数据流处理

    Spring Cloud Stream的核心是Stream,准确来讲Spring Cloud Stream提供了一整套数据流走向(流向)的API, 它的最终目的是使我们不关心数据的流入和写出,而只关心对数据的业务处理,本文给大家介绍了Spring Cloud Stream实现数据流处理,需要的朋友可以参考下
    2024-11-11
  • 在Java的Struts框架中ONGL表达式的基础使用入门

    在Java的Struts框架中ONGL表达式的基础使用入门

    这篇文章主要介绍了深入解析在Java的Struts框架中ONGL表达式的基础使用入门,Struts框架是Java的SSH三大web开发框架之一,需要的朋友可以参考下
    2015-11-11
  • Mybatis中SQL的执行过程详解

    Mybatis中SQL的执行过程详解

    MyBatis框架通过映射文件或注解将Java代码中的方法与数据库操作进行映射,执行过程包括SQL解析、参数绑定、SQL预编译、执行、结果映射、事务处理、缓存处理和日志记录
    2024-12-12
  • Java 基础之NIO 学习详解

    Java 基础之NIO 学习详解

    这篇文章主要介绍了java基础之NIO介绍及使用,文中有非常详细的代码示例,对正在学习java基础的小伙伴们有非常好的帮助,需要的朋友可以参考下
    2021-09-09
  • Java 精炼解读类和对象原理

    Java 精炼解读类和对象原理

    面向对象乃是Java语言的核心,是程序设计的思想。Java语言的面向对象技术包括了面向对象和面向过程的基本概念,面向对象的特征,Java语言的类,对象,修饰符,抽象类等一系列的知识点
    2022-03-03

最新评论