歡迎訪問(wèn)我的博客,同步更新: 楓山別院
源代碼版本2.4.5-SNAPSHOT
⑧還回連接
這節(jié)我們要分析一下將數(shù)據(jù)庫(kù)連接還回到連接池的方法requite。
請(qǐng)看代碼:
/**
* 該方法將借出去的連接還回到連接池中
* 不通過(guò)該方法還回的連接會(huì)造成內(nèi)存泄露
*
* @param bagEntry the value to return to the bag
* @throws NullPointerException if value is null
* @throws IllegalStateException if the requited value was not borrowed from the bag
*/
public void requite(final T bagEntry) {
//⑧
//lazySet方法不能保證連接會(huì)立刻被設(shè)置成未使用狀態(tài), 這是個(gè)延遲方法
//這是一種優(yōu)化, 如果要立即生效的話, 可能會(huì)需要使用volatile等, 讓其他線程立即發(fā)現(xiàn), 這會(huì)降低性能, 使用lazySet浪費(fèi)不了多少時(shí)間, 但是不會(huì)浪費(fèi)性能
bagEntry.lazySet(STATE_NOT_IN_USE);
//⑨
//將連接放回到threadLocal中
final List<Object> threadLocalList = threadList.get();
if (threadLocalList != null) {
threadLocalList.add(weakThreadLocals ? new WeakReference<>(bagEntry) : bagEntry);
}
//通知等待線程, 有可用連接
synchronizer.signal();
}
一般我們都是通過(guò) Spring 來(lái)使用 HikariCP 的,自己動(dòng)手啟動(dòng)一個(gè)連接池的機(jī)會(huì)還是少。在 Spring 中使用非常方便,一切都是 Spring 幫我們搞定,我們只管使用,所以需要將連接還回連接池的機(jī)會(huì)也比較少,也有可能你是間接用過(guò),比如從 HikariCP 中借用的連接,用完之后調(diào)用了 close方法,連接其實(shí)并沒(méi)有真正的被關(guān)閉,而是還回了連接池,真正的close方法被 HikariCP 重寫(xiě)了。其實(shí)這是一個(gè)至關(guān)重要的方法,如果借用出去的連接,不通過(guò)這個(gè)方法還回來(lái),會(huì)導(dǎo)致內(nèi)存泄露的。
我們來(lái)分析下這個(gè)方法。
bagEntry.lazySet(STATE_NOT_IN_USE);這個(gè)很眼熟,我們?cè)谇懊嬉?jiàn)過(guò)compareAndSet方法,從字面意思理解,這是一個(gè)延遲修改狀態(tài)的方法,lazySet方法不能保證連接會(huì)立刻被設(shè)置成未使用狀態(tài), 這是個(gè)延遲方法,因?yàn)檫@是將連接還回去,時(shí)效要求并不是那么高,延遲個(gè)幾十幾百毫秒,對(duì)用戶沒(méi)有任何影響。反之,如果要立即讓狀態(tài)生效,讓其他線程立即能發(fā)現(xiàn)的話,那么可能要使用volatile等,這可能會(huì)得不償失。
⑨放到線程本地threadList
我們前面說(shuō)過(guò),還回去的連接也會(huì)放到線程本地的ThreadLocal中,方便該線程再次請(qǐng)求連接的時(shí)候,可以節(jié)省時(shí)間,提高性能。
這里的synchronizer.signal();方法,是通知其他線程有可用的連接加入到連接池了。這里的通知并不是線程間通信的那個(gè)通知,只是計(jì)數(shù)器加 1 了而已,我們?cè)谏弦还?jié)里提過(guò),在循環(huán)遍歷完連接池沒(méi)有拿到連接之后,是會(huì)檢查這個(gè)synchronizer的值,如果比循環(huán)之前變大了,就是有可用連接加入到連接池了,這里是其中一個(gè)修改synchronizer的地方,還有創(chuàng)建連接之后,也會(huì)將synchronizer加 1。
⑩添加連接
向連接池中添加一個(gè)連接。
public void add(final T bagEntry) {
if (closed) {
LOGGER.info("ConcurrentBag has been closed, ignoring add()");
throw new IllegalStateException("ConcurrentBag has been closed, ignoring add()");
}
//⑩
sharedList.add(bagEntry);
synchronizer.signal();
}
這里的bagEntry是一個(gè)連接的包裝對(duì)象,添加一個(gè)的話,就是加入到sharedList中,而synchronizer.signal();的作用我們上面剛剛分析過(guò)了,就是可用連接的計(jì)數(shù)器加 1。
?移除連接
該方法是從連接池中移除一個(gè)連接,是真正的刪除。
public boolean remove(final T bagEntry) {
//?
//嘗試標(biāo)記移除使用中和保留狀態(tài)的連接, 如果標(biāo)記失敗, 就是空閑的連接, 直接返回 false
//也就是檢查連接的狀態(tài), 不能移除空閑的連接或者已經(jīng)標(biāo)記移除的連接
if (!bagEntry.compareAndSet(STATE_IN_USE, STATE_REMOVED) && !bagEntry.compareAndSet(STATE_RESERVED, STATE_REMOVED) && !closed) {
LOGGER.warn("Attempt to remove an object from the bag that was not borrowed or reserved: {}", bagEntry);
return false;
}
//如果上面標(biāo)記成功了, 那么從連接池中移除這個(gè)連接
final boolean removed = sharedList.remove(bagEntry);
if (!removed && !closed) {
LOGGER.warn("Attempt to remove an object from the bag that does not exist: {}", bagEntry);
}
// synchronizer.signal();
return removed;
}
這個(gè)remove方法,并不是能移除所有的連接,它只能移除兩種狀態(tài)的連接,分別是STATE_IN_USE和STATE_RESERVED。我們看?處的代碼,如果不是這兩個(gè)狀態(tài),那么會(huì)直接打印警告,移除不了。連接有四個(gè)狀態(tài),除了這兩個(gè),還有就是已刪除狀態(tài),自然不能再次刪除了;還有一個(gè)就是未使用狀態(tài)了,也就是說(shuō),我們要移除一個(gè)未使用狀態(tài)的連接,那是不行的。
后面就簡(jiǎn)單了,如果狀態(tài)修改成功了,那么就從連接池中刪除這個(gè)連接就可以了,收工!
至此,ConcurrentBag中重要的方法我們就分析完了,歡迎大家一起討論。