黑马程序员技术交流社区
标题:
C3P0连接池解析
[打印本页]
作者:
大蓝鲸Java
时间:
2019-3-22 13:27
标题:
C3P0连接池解析
C3P0连接池解析
1
,连接池的原理和意义
不管什么样的连接池其核心意义都是一致的,就是为一个项目创建连接对象,保存在池中待用。每次访问时从池中
获取已经存在的连接,使用完毕后,再返回池中。从而减少了创建连接和销毁连接而浪费的系统资源。本文会分析
目前常用连接池之一
C3P0
。
2
,
C3P0
简单配置
c3p0
连接池作为我们开发中最为常用的连接池之一使用起来是非常简单的。本文将从原理角度来分析一下
C3P0
连
接池的主要架构。
一般只需要配置以下参数即可:
通常情况下,我们还会跟本地配置文件
properties
或者本地
xml
文件相结合的方式进行操作,以增强代码的灵活
性。
3
,核心类
C3P0
连接池主要核心类有以下几个:
ComboPooledDataSource
:主要负责连接池的配置和获取池中的连接
C3P0PooledConnectionPoolManager
:大内总管的角色。
负责生命周期以及生命周期所需要的一些参数。
比如:
AdminTaskTimer
定时检测和全局的线程池
C3P0PoolConnectionPool
:
表示物理连接池,本身的业务逻辑相对于比较简单,主要核心是通过代理
ResourcePool
来实现逻辑的。
BasicResourcePool
:
是
BasicResourcePool
这个接口的实现类,是大内总管
C3P0PooledConnectionPoolManager
的手下。
大内总管管池子,而
BasicResourcePool
是管池中连接的生命周期。
相当于大内中长春宫,宁寿宫等每一个宫的管事。
String
driverClass
=
"com.mysql.jdbc.Driver"
;
String
jdbcURL
=
"jdbc:mysql://localhost:3306/test"
;
String
user
=
"root"
;
String
password
=
""
;
ComboPooledDataSource cpds
=
new
ComboPooledDataSource
();
cpds
.
setDriverClass
(
driverClass
);
cpds
.
setJdbcUrl
(
jdbcURL
);
cpds
.
setUser
(
user
);
cpds
.
setPassword
(
password
);
需要用的时候再获得其中的连接即可。
Connection conn
=
cpds
.
getConnection
();
BasicResourcePool.Manager
:
是
BasicResourcePool
的一个助手,协助一起管理连接池中的连接。
在
BasicResourcePool
中有几个任务需要注意:
1
,创建连接
ScatteredAcquireTask
2
,销毁连接
RemoveTask
3
,检测连接是否过期
CullTask
4
,检测连接是否空闲
CheckIdleResourcesTask
4
,初始化特点
C3P0
连接池在初始化的时候并不是在创建的时候直接创建并进行初始化的,连接池采取了一种
“
懒初始化
”
的思
想。那么何为懒初始化呢?
自己本身不会不会主动初始化,而是会等到第一个连接请求进来之后它才会初始化。而那个触发点就是:
当代码执行到这里,即获取到一个连接时,才会进行初始化保证了资源的有效利用。
5
,创建连接从而降低系统资源消耗
众所周知,使用连接池中的连接是为了降低系统资源消耗,那么究竟是为什么呢?
首先数据库其实是一个管理系统,以
MySQL
为例,
MySQL
是一个数据库管理系统,区分服务端和客户端,
MySQL
指的是这个整体管理系统,所以想要实际的把数据存入到表中,需要经历以下过程。
建立
TCP
连接,然后通过三次握手协议发送与相应;其次客户端需要进行账户验证,服务器返回确认;用户验证
后,需要传输相关连接变量如是否自动提交事务的设置等。
所以总而言之,操作数据之前,一定会有多次数据的交互,然后才能执行真正的执行
CURD
的
sql
语句。
连接池真正的降低的是以上的这些步骤,当需要对数据库进行操作时,直接获取已经存在的,就减少了资源的消
耗。
预先建立多个数据库连接对象,存入到池中。当有客户端进行请求的时候,取出一个连接为当前客户端的请求而操
作数据库,当该客户端请求操作完毕时,将请求放回连接池里。一直没有关闭。
6
,连接池降低消耗的原理图解
ComboPooledDataSource cpds
=
new
ComboPooledDataSource
();
cpds
.
getConnection
();
北京市昌平区建材城西路金燕龙办公楼一层 电话:400-618-9090
如果没有连接池,那么访问者跟数据库之间每次都创建一个连接,用完再销毁。
如果用了连接池,就不会自己创建,而是从池子中拿,用完再进行归还。
7
,入口类
ComboPooledDataSource
在进行初始化创建的时候,会有池子的入口类,就是
ComboPooledDataSource
。其中
gettConnection
方法是在
AbstractPoolBackedDataSource
类中实现。
其中
PooledConnection
是
javax.sql
下的一个接口。针对于这个连接的执行者对象,结果集对象和连接对象对被
pooledconnection
管理。而
c3p0
实现是
NewPooledConnection
。
8
,
BasicResourcePool
类
8.1
,主要核心代码:
其中
timeout
表示连接溢出时间。
查看池中是否有未使用的
connection
,如果有就返回,除此之外,还需要判断连接是否空闲通过
timeout
判断连接
是否过期,如果没有最大数,就生成一个,或者就等待。
8.2
,主要核心代码:
AbstractPoolBackedDataSource
类的
getConnection
方法如下:
public
Connection
getConnection
()
throwsSQLException
{
PooledConnection pc
=
getPoolManager
().
getPool
().
checkoutPooledConnection
();
returnpc
.
getConnection
();
}
O
bject
resc
=
prelimCheckoutResource
(
timeout
);
booleanrefurb
=
attemptRefurbishResourceOnCheckout
(
resc
);
得到连接后,检测连接的可用性。 如果连接可用,接着判断连接是否处于管理中,如果不在就再调用本方法获取一
个,如果在,就返回本连接。
8.3
,添加连接
获取连接时由
BasicResourcePool
类中的
prelimCheckoutResource
方法触发,调用过程如下:
1
,
BasicResourcePool
类
prelimCheckoutResource
方法当可用连接数为
0
时调用
recheckResizePool
方法。
2
,
echeckResizePool
方法判断需要扩容则调用
expandPool
方法。
expandPool
方法使用线程池调用
AcquireTask
进行扩容。
3
,
AcquireTask
调用
mgr.acquireResource
方法获取连接,如果连接池里的连接未到最大数量将获取的连接加入管
理和未使用的
list
中,否则销毁连接。
8.4
,回收连接
由
close
方法触发。调用过程如下:
1
,
C3P0PooledConnectionPool
类
,
的
ConnectionEventListenerImpl
监听器类的
connectionClosed
方法被触发。
2
,判断同步还是异步的(默认是同步)。如果是异步,那么就采用线程池来进行处理回收,如果是同步,那么就
进行直接调用
BasicResourcePool
类的
checkinResource
方法对连接进行回收,此处的回收并不是销毁,而是归
还。
9
,死锁机制
在
C3P0
中有很多任务都是采取多线程进行处理的。比如添加链接或者是回收连接。一旦有这种类似于生产者消费
者的多线程任务,那么就必须要进行死锁判断,否则会进入无线等待状态。
该机制的是由
ThreadPoolAsynchronousRunner
类的子类
DeadlockDetector
来执行具体的操作的。、
DeadlockDetector
类继承了
TimerTask
类,由其中的
Timer
来调用
DeadlockDetecto
运行。
DeadlockDetector
类中
有两个属性:
LinkedListlast
和
LinkedListcurrent
。
这两个
LinkedList
集合放的是等待执行的任务。根据他的属性名字就很轻易理解,
last
是上次检测时待执行的任务
而
current
是本次检测时要执行的任务,当
pendingTasks
不为空且
current.equals( last)
时认为死锁发生。当一旦确
认死锁发生了,那么会把当前任务交给
ThreadPerTaskAsynchronousRunner
类来处理
,
这个类会启用新的线程
DispatchThread
来处理当前的任务队列。
总结:
连接池可以在访问数据库时降低系统消耗。所以不管是什么项目都必定会有连接池的存在。
连接池基本使用非常的简单,通过一些简单的配置即可,但是想要真正把连接池原理理解,建议等用熟之后,闲暇
之余再进行研究。
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/)
黑马程序员IT技术论坛 X3.2