【思考】
1、连接池作用:估计都知道,保存数据库连接,避免下次请求时再新建耗时。
2、连接池存什么? 平常可能不太注意,仔细翻翻源码稍微思考一下就知道了。Mysql连接是mysql-connector-java.jar提供的,他给我返回Connection实例。而连接池存的就是Connection实例,高效的提供给多线程使用。
3、那到底跟我们自己List存一下有什么区别呢?也就是连接池解决什么问题、如何高效:多线程时,每个线程使用一个Connection,可能有100个线程同时getConnection,我们只有10个,那就需要让有的线程等待。这就是要解决的并发问题。至于如何高效,emmm…HikariCP优化了很多细节,这里不研究。我今天只说说它的主要流程,也是自己看过后的总结,希望帮助大家入门。
【主要类】
1、HikariDataSource:就是一个DataSource,包含HikariPoo成员变量
2、HikariPool:HikariCP的入口,包含了ConcurrentBag成员变量
3、ConcurrentBag:用于高效处理并发的类,包含了ThreadLocal<List<T>> threadList、CopyOnWriteArrayList<T> sharedList、SynchronousQueue<T> handoffQueue。这三个变量就是核心,他们是储存Connection的地方。
4、PoolEntry:封装储存Connection的类,可以理解一个PoolEntry就是一个Connection。
【如何管理的】
哈哈,其实我就是想说说ConcurrentBag里面的那三个变量,可以从这三个地方获取到Connection。
ThreadLocal<List> threadList 可以称之为“第一选择”。
一个线程获取到Connection用完后,先存在ThreadLocal,下次就可以直接先用了,如果去sharedList里拿,还要对waiter这个AotmicInteger进行CAS操作,直接拿更快。
CopyOnWriteArrayList sharedList 可以称之为“第二选择”。
所有创建的Connection都储存在这个list中。
SynchronousQueue handoffQueue 可以称之为“最后的选择”。
如果以上两个地方都拿不到PoolEntry,那就来这个队列等着。等到天荒地老,直到timeout超时。
【流程图讲解】
1、不管哪个线程池,获取Connection接口都是DataSource.getConnection()
2、之后会调用到HikariCP的核心ConcurrentBag.borrow方法。开始了它的选择之路,也就是我说的三种选择。
3、第一选择:ThreadLoacl。第一次肯定是没有的,如果之前用过,那第二次就可以直接用。不过用之前要先看看PoolEntry的状态,因为,别的线程可能已经通过sharedList“偷”走了它。
4、第二选择:SharedList。遍历看看PoolEntry的状态,如果有未使用的,那就拿过来用。因为可能会偷了别家线程的,所以要给Listener发个消息,通知创建一个add任务新建Connection。
5、第三选择,最后的倔强:handoffQueue。如果现有的PoolEntry都不可用,那就要执行一次add任务了,跟上面的方法相同。通知完,就doWhile起来,在那等,add任务创建的PoolEntry或者其他方法归还的PoolEntry都会放到队列中。如果等不到,那timeout了就抛异常了。
6、归还。ProxyConnection.close()方法,补充一下ProxyConnection是对PoolEntry的包装。偶买噶。最终会调用bag的requite[翻译为“报答、归还”吧]方法。改方法有两个去向,当waiter>0时,就给队列,否则就放到ThreadLoacl里。
【后续】
太复杂了,如果还有后续的话。。。。