今天来看下DruidDataSource
里init()
方法剩下的线程创建部分代码,本来说第二天看的,拖到了第三天哈哈。。。
代码解读
这块其实是这个init()
代码最关键的部分,因为涉及到连接的创建还有各种active count
,pool count
,minIdle count
,maxActive
等数量的计算和判断逻辑,当然里面还有一些更细化的维度,比如poolingPeak
等,还有一些常用的配置参数会在这里面进行初始化,产生作用。
如上图经过前面校验后,会开始进行连接的创建,根据传入的CreateScheduler
和AsyncInit
值决定是异步创建还是同步创建。
首先看下异步创建,直接创建initialSize
个数的连接,其主要实现是在CreateConnectionTask
这个线程的runInternal()
方法里,核心代码如下:
当有等待线程时,判断边界条件,poolingCount >= notEmptyWaitThreadCount
注意这里的poolingCount
不是指连接池连接上限,而是当前空闲连接数,这个我一开始理解错了, 连接池上限是poolingPeak
这个,用activeCount + poolingCount < minIdle
而不是poolingPeak<minIdle
是因为里面可能有丢弃的连接,这样会导致poolingPeak
不准确。
注意这里只是锁住了连接数量的更改,而没有去锁连接创建的代码,就是为了最大程度的进行并发。
createPhysicalConnection()
创建物理连接时会初始化Username
,Password
,AutoCommit
,ReadOnly
,TransactionIsolation
等属性,如果有设置就用你设置的,否则用默认的。
后续会执行connectionInitSqls
,这个是druid在连接初始化时会执行的sql,一般可以将一些需要提前初始化的sql放这里比如这种set names utf8mb4;
很奇怪的是专门用了variables
和globalVariables
两个map将数据库里面的show variables
和show global variables
对应参数装起来,不知道干嘛用的,但是PhysicalConnectionInfo
这个类里面专门会存储,估计是一个比较重要的东西,后面看到再说。
将这些连接放到DruidConnectionHolder
这个对象里面,构造函数如下:
对象里面有dataSource
,connect
,刚才说的两个map,还有一个时间。将其整个放到DruidConnectionHolder[]
数组这样一个连接池的底层实现里。
同步的实现就更粗暴简单了,直接循环创建连接,拿到真实物理连接之后封装进PhysicalConnectionInfo
,在装进DruidConnectionHolder
里,最后扔进DruidConnectionHolder[]
线程池里。
思考和疑问
至此整个DruidDataSource
类差不多理清楚了,但是还有很多细节没太想明白,比如:
createAndStartCreatorThread()
和createAndStartDestroyThread()
这两个守护线程是干嘛的?为啥要设成守护线程而不是直接的线程?DruidPooledConnection
里面又有conn和holder,conn可以通过holder的getConnection()拿到,为啥要单独设置一个成员变量?意义何在?- Filter的作用和实现
- Wrapper干嘛的(好像是JDBC基础知识?)
最后
后续按照上面问题的线一个个解决吧。