A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

C3P0连接池解析

1.1连接池的原理和意义

不管什么样的连接池其核心意义都是一致的,就是为一个项目创建连接对象,保存在池中待用。每次访问时从池中
获取已经存在的连接,使用完毕后,再返回池中。从而减少了创建连接和销毁连接而浪费的系统资源。本文会分析
目前常用连接池之一C3P0。

1.2C3P0简单配置

c3p0连接池作为我们开发中最为常用的连接池之一使用起来是非常简单的。
本文将从原理角度来分析一下C3P0连接池的主要架构。

一般只需要配置以下参数即可:通常情况下,我们还会跟本地配置文件properties或者本地xml文件相结合的方式进行操作,以增强代码的灵活性。

1.3核心类

C3P0连接池主要核心类有以下几个:
ComboPooledDataSource:

主要负责连接池的配置和获取池中的连接

C3P0PooledConnectionPoolManager:

大内总管的角色。负责生命周期以及生命周期所需要的一些参数。

比如:AdminTaskTimer定时检测和全局的线程池

C3P0PoolConnectionPool

表示物理连接池,本身的业务逻辑相对于比较简单,主要核心是通过代理ResourcePool来实现逻辑的。

[AppleScript] 纯文本查看 复制代码
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这个接口的实现类,是大内总管C3P0PooledConnectionPoolManager的手下。大内总管管池子,而BasicResourcePool是管池中连接的生命周期。相当于大内中长春宫,宁寿宫等每一个宫的管事。
BasicResourcePool.Manager:是BasicResourcePool 的一个助手,协助一起管理连接池中的连接。

在BasicResourcePool 中有几个任务需要注意:

1,创建连接ScatteredAcquireTask

2,销毁连接RemoveTask

3,检测连接是否过期CullTask

4,检测连接是否空闲CheckIdleResourcesTask

1.4C3P0初始化特点

C3P0连接池在初始化的时候并不是在创建的时候直接创建并进行初始化的,连接池采取了一种“懒初始化”的思想。那么何为懒初始化呢?
自己本身不会不会主动初始化,而是会等到第一个连接请求进来之后它才会初始化。而那个触发点就是:当代码执行到这里,即获取到一个连接时,才会进行初始化保证了资源的有效利用。

1.5创建连接从而降低系统资源消耗

众所周知,使用连接池中的连接是为了降低系统资源消耗,那么究竟是为什么呢?首先数据库其实是一个管理系统,以MySQL为例,MySQL是一个数据库管理系统,区分服务端和客户端,MySQL指的是这个整体管理系统,所以想要实际的把数据存入到表中,需要经历以下过程。首先建立TCP连接,然后通过三次握手协议发送与相应;其次客户端需要进行账户验证,服务器返回确认;用户验证后,需要传输相关连接变量如是否自动提交事务的设置等。所以总而言之,操作数据之前,一定会有多次数据的交互,然后才能执行真正的执行CURD的sql语句。

连接池真正的降低的是以上的这些步骤,当需要对数据库进行操作时,直接获取已经存在的,就减少了资源的消耗。预先建立多个数据库连接对象,存入到池中。当有客户端进行请求的时候,取出一个连接为当前客户端的请求而操作数据库,当该客户端请求操作完毕时,将请求放回连接池里。一直没有关闭。

1.6入口类ComboPooledDataSource
在进行初始化创建的时候,会有池子的入口类,就是ComboPooledDataSource。其中gettConnection方法是在
AbstractPoolBackedDataSource类中实现。
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.getConnection();
其中PooledConnectionjavax.sql下的一个接口。针对于这个连接的执行者对象,结果集对象和连接对象对被
pooledconnection管理。而c3p0实现是NewPooledConnection
1.7BasicResourcePool
1,主要核心代码:Object resc = prelimCheckoutResource(timeout); 其中timeout表示连接溢出时间。
查看池中是否有未使用的
connection,如果有就返回,除此之外,还需要判断连接是否空闲通过timeout判断连接
是否过期,如果没有最大数,就生成一个,或者就等待。
2,主要核心代码: booleanrefurb = attemptRefurbishResourceOnCheckout(resc); 得到连接后,检测连
接的可用性。 如果连接可用,接着判断连接是否处于管理中,如果不在就再调用本方法获取一个,如果在,就返
回本连接。
3,添加连接
获取连接时由
BasicResourcePool类中的prelimCheckoutResource方法触发,调用过程如下:
首先,
BasicResourcePoolprelimCheckoutResource方法当可用连接数为0时调用recheckResizePool方法。而
recheckResizePool方法判断需要扩容则调用expandPool方法。expandPool方法使用线程池调用AcquireTask
进行扩容。最后 AcquireTask调用mgr.acquireResource方法获取连接,如果连接池里的连接未到最大数量将获取
的连接加入管理和未使用的
list中,否则销毁连接。
4,回收连接
close方法触发。调用过程如下:
首先,
C3P0PooledConnectionPool类里的ConnectionEventListenerImpl监听器类的connectionClosed方法被
触发。然后判断同步还是异步的(默认是同步)。如果是异步,那么就采用线程池来进行处理回收,如果是同步,
那么就进行直接调用
BasicResourcePool类的checkinResource方法对连接进行回收,此处的回收并不是销毁,而
是归还。
1.8死锁机制
C3P0中有很多任务都是采取多线程进行处理的。比如添加链接或者是回收连接。一旦有这种类似于生产者
消费者的多线程任务,那么就必须要进行死锁判断,否则会进入无线等待状态。
该机制的是由
ThreadPoolAsynchronousRunner类的子类DeadlockDetector来执行具体的操作的。
DeadlockDetector类继承了TimerTask类,由其中的Timer来调用DeadlockDetecto运行。DeadlockDetector
中有两个属性:
LinkedList last LinkedList current
AbstractPoolBackedDataSource类的getConnection方法如下:
public Connection getConnection() throwsSQLException {
PooledConnection pc = getPoolManager().getPool().checkoutPooledConnection();
returnpc.getConnection();
}
这两个LinkedList集合放的是等待执行的任务。根据他的属性名字就很轻易理解,last是上次检测时待执行的
任务而
current是本次检测时要执行的任务,当pendingTasks不为空且current.equals( last )时认为死锁发生。
当一旦确认死锁发生了,那么会把当前任务交给
ThreadPerTaskAsynchronousRunner类来处理,这个类会启
用新的线程
DispatchThread来处理当前的任务队列。

0 个回复

您需要登录后才可以回帖 登录 | 加入黑马