在平常的开发中,通常将一些对象保存起来,主要考虑对象的创建成本。如线程资源、数据库连接资源、TCP连接资源等,这类对象的初始化通常需要花费很长的时间,若频繁的申请和销毁,就会耗费大量的系统资源,造成不必要的性能损失。这类对象都有一个显著的特征,就是通过轻量级的重置工作,可以循环、重复的使用。这个时候,可以使用一个虚拟的池子,将这些资源保存起来,当使用的时候,就从池子里快速获取一个即可。
在java中,池化技术应用广泛,典型的有数据库连接池、线程池,下面对数据库连接池进行分析。
1、Commons Pool 2
Commons Pool 2为java中共用的池化包。其依赖如下

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.11.1</version>
</dependency>

GenericObjectPool 是对象池的核心类,通过传入一个对象池的配置和一个对象的工厂,即可快速创建对象池。

redis的常用客户端Jedis就是使用Commons Pool管理连接池的,Jedis使用工厂创建对象,对象工厂类最主要的方法就是makeObject,返回值的PooledObject 类型,可以将对象使用 new DefaultPooledObject<>(obj) 进行简单包装返回。

超时时间设置为接口可以忍受的最大延迟,即最大等待时间。一个正常服务响应时间10ms左右,达到1s就会感觉到卡顿,那么这个超时参数设置成500-1000ms都是可以的,超时之后,会抛出异常,请求快速失败,不会影响其他业务线程,这种fail fast思想在互联网应用中非常广泛。

2、HikariCP连接池

springboot中默认的数据库连接池。

数据库是开发中经常使用的组件,针对数据库设计的客户端连接池也非常多,设计原理也基本一样,为了减少数据库连接创建、消耗的资源消耗。但是不通的连接池的性能也是有差别的,夏天是HikariCP官网的一张测试图,可以看到其性能对比。

数据连接池查询mysql命令 数据库连接池hikaricp_数据库


HikariCP为什么快呢?

  • 使用 FastList 替代 ArrayList,通过初始化的默认值,减少了越界检查的操作
  • 优化并精简了字节码,通过使用 Javassist,减少了动态代理的性能损耗,比如使用 invokestatic 指令代替 invokevirtual 指令
  • 实现了无锁的 ConcurrentBag,减少了并发场景下的锁竞争

将对象池化之后,只是开启了第一步优化。要想达到最优性能,就不得不调整池的一些关键参数,合理的池大小加上合理的超时时间,就可以让池发挥更大的价值。和缓存的命中率类似,对池的监控也是非常重要的。

连接池的关键特征和原理还需继续理解,在现实工程中如何解决问题。