池化技术的理解(线程池,对象池,数据库连接池)

文章首先通过一个通俗的实例帮助读者理解池化思想,随后说明池化思想在各种技术中的体现。

(欢迎指正)
原文出处:blog.csdn.net/TheSnowBo

例子 - 为何要用池?

先举一个简单的使用篮球 例子,我们有多种策略使用篮球,并且使用篮球之后会产生一定的代价,主观上认为我们倾向于将代价最小化。

策略1:(一次性使用)

这是一种比较笨的策略,每次都买一个新的篮球用于使用,使用之后丢掉。于是我们可以得到如下代价公式:

总代价=(买篮球代价+用篮球代价)∗使用次数


策略2:(重复使用)

在该策略中,认为篮球是可以多次使用的。于是我们可以得到如下公式::

总代价=买篮球代价∗篮球个数+重复代价∗使用次数+用篮球代价∗使用次数


策略的选择

上面列举了两种策略,事实上还有很多其他的策略。

两种策略本身是没有绝对的好坏的,而是视场景而定的。但就现实情况而言(比如,使用次数很多),篮球这个例子中符合如下的规律:

买篮球的代价∗篮球个数+复用的代价∗使用次数<买篮球的代价∗使用次数


这意味着,复用的总代价 小于 不复用的代价,即复用的策略更适合篮球这个例子


而策略2反映的就是一种池化的思想,“复用的代价”远远小于“创建的代价”,所以此处通过池化技术增加“复用”、减少“创建”。
PS:有些场景下 “创建的代价”远远小于 “复用的代价”,此时就应该采用策略1的方式了。

技术实例

结合上面提到的主要思想,分别看看在编程中实际用到的一些池技术。

对象池

对象创建的代价

这里我们主要以java为代表,在java中创建对象,主要包括如下几个步骤:


  1. 根据new 等标识符后面的参数,在常量池查找类的符号引用。
  2. 如果没找到符号应用(类并未加载),进行类的加载、解析、初始化等。
  3. 虚拟机根据类,为对象在堆中分配内存,并将分配的内存初始化为0。针对对象头,建立相应的描述结构。(耗时操作:需要查找堆中的空闲区域,修改内存分配状态等)
  4. 调用对象的初始化方法。(耗时操作:用户的复杂的逻辑验证等操作,如IO、数值计算是否符合规定等等)

对象池的优势

通过对象创建的例子,可看出对象创建是一个复杂的过程,少数的对象的创建并不会影响程序的太多的性能,但是如果达到了数以万计,就应该考虑复用同类对象的分配了。
【通俗理解】只是替换某个已存在对象的状态(填充原对象结构中的变量),复用已存在对象分配的内存(节省了寻找空闲堆区等方面的时间)。

相当于给内存重新换了身衣服
这里认为,重新创建对象的代价 远远大于 更换已存在对象中相关的状态变量


PS: 线程池,数据库连接池存在的理由也同样如此。

发布于 2017-12-20