十、HikariCP源碼分析之ConcurrentBag三

歡迎訪問(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_USESTATE_RESERVED。我們看?處的代碼,如果不是這兩個(gè)狀態(tài),那么會(huì)直接打印警告,移除不了。連接有四個(gè)狀態(tài),除了這兩個(gè),還有就是已刪除狀態(tài),自然不能再次刪除了;還有一個(gè)就是未使用狀態(tài)了,也就是說(shuō),我們要移除一個(gè)未使用狀態(tài)的連接,那是不行的。

后面就簡(jiǎn)單了,如果狀態(tài)修改成功了,那么就從連接池中刪除這個(gè)連接就可以了,收工!

至此,ConcurrentBag中重要的方法我們就分析完了,歡迎大家一起討論。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容