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 数据库连接池内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 通过jenkins发布java项目到目标主机上的详细步骤

    通过jenkins发布java项目到目标主机上的详细步骤

    这篇文章主要介绍了通过jenkins发布java项目到目标主机上的详细步骤,发布java项目的步骤很简单,通过拉取代码并打包,备份目标服务器上已有的要发布项目,具体内容详情跟随小编一起看看吧
    2021-10-10
  • Java命令设计模式详解

    Java命令设计模式详解

    这篇文章主要为大家详细介绍了Java命令设计模式,对命令设计模式进行分析理解,感兴趣的小伙伴们可以参考一下
    2016-02-02
  • java多线程编程之使用thread类创建线程

    java多线程编程之使用thread类创建线程

    在Java中创建线程有两种方法:使用Thread类和使用Runnable接口。在使用Runnable接口时需要建立一个Thread实例
    2014-01-01
  • 五分钟解锁springboot admin监控新技巧

    五分钟解锁springboot admin监控新技巧

    本文不会讲如何搭建企业的运维监控系统,有兴趣的可以去找找成熟的比如Zabbix、Prometheus,甚至比较简单的Wgcloud都能满足一定的需求,不在此赘述。本文讲解如何使用Springboot admin对spring boot项目进行应用监控,感兴趣的朋友一起看看吧
    2021-06-06
  • Druid监控分布式实现过程解析

    Druid监控分布式实现过程解析

    这篇文章主要介绍了Druid监控分布式实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-11-11
  • 通过openpyxl读取excel文件过程解析

    通过openpyxl读取excel文件过程解析

    这篇文章主要介绍了通过openpyxl读取excel文件过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-02-02
  • Java中的break和continue关键字的使用方法总结

    Java中的break和continue关键字的使用方法总结

    下面小编就为大家带来一篇Java中的break和continue关键字的使用方法总结。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-11-11
  • maven报错:Failed to execute goal on project问题及解决

    maven报错:Failed to execute goal on p

    这篇文章主要介绍了maven报错:Failed to execute goal on project问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-04-04
  • 解决tk.mybatis中写自定义的mapper的问题

    解决tk.mybatis中写自定义的mapper的问题

    这篇文章主要介绍了使用tk.mybatis中写自定义的mapper的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • Java建造者设计模式详解

    Java建造者设计模式详解

    这篇文章主要为大家详细介绍了Java建造者设计模式,对建造者设计模式进行分析理解,感兴趣的小伙伴们可以参考一下
    2016-02-02

最新评论