在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

讀懂HikariCP一百行代碼,多線程就是個孫子!

jf_ro2CN3Fa ? 來源:芋道源碼 ? 作者:芋道源碼 ? 2022-12-14 15:45 ? 次閱讀


總結(jié):Java屆很難得有讀百十行代碼就能增加修煉的機會,這里有一個。

通常,我在看書的時候一般不寫代碼,因為我的腦袋被設(shè)定成單線程的,一旦同時喂給它不同的信息,它就無法處理。

但多線程對電腦來說就是小菜一碟,它可以同時做很多事,看起來匪夷所思。好希望把自己的大腦皮層移植到這些牛x的設(shè)備上。

用人腦思考電腦正在思考的問題,這本身就是一種折磨。但平常的工作和面試中,又不得不面對這樣的場景,所以多線程就成了編程路上一塊難啃的骨頭。

HikariCP是SpringBoot默認的數(shù)據(jù)庫連接池,它毫不謙虛的的起了一個叫做的名字,這讓國產(chǎn)Druid很沒面子。

還是言歸正傳,看一下Hikari中的ConcurrentBag吧。

核心數(shù)據(jù)結(jié)構(gòu)

多線程代碼一個讓人比較頭疼的問題,就是每個API我都懂,但就是不會用。很多對concurrent包倒背如流的同學(xué),在面對現(xiàn)實的問題時,到最后依然不得不被迫加上Lock或者synchronized。

ConcurrentBag是一個Lock free的數(shù)據(jù)結(jié)構(gòu),主要用作數(shù)據(jù)庫連接的存儲,可以說整個HikariCP的核心就是它。刪掉亂七八糟的注釋和異常處理,可以說關(guān)鍵的代碼也就百十來行,但里面的道道卻非常的多。

ConcurrentBag速度很快,要達到這個目標(biāo),就需要一定的核心數(shù)據(jù)結(jié)構(gòu)支持。

privatefinalCopyOnWriteArrayListsharedList;
privatefinalThreadLocal>threadList;
privatefinalAtomicIntegerwaiters;
privatefinalSynchronousQueuehandoffQueue;
  • sharedList 用來緩存所有的連接,是一個CopyOnWriteArrayList結(jié)構(gòu)。
  • threadList 用來緩存某個線程所使用的所有連接,相當(dāng)于快速引用,是一個ThreadLocal類型的ArrayList。
  • waiters 當(dāng)前正在獲取連接的等待者數(shù)量。AtomicInteger,就是一個自增對象。當(dāng)waiters的數(shù)量大于0時候,意味著有線程正在獲取資源。
  • handoffQueue 0容量的快速傳遞隊列,SynchronousQueue類型的隊列,非常有用。

ConcurrentBag里面的元素,為了能夠無鎖化操作,需要使用一些變量來標(biāo)識現(xiàn)在處于的狀態(tài)。抽象的接口如下:

publicinterfaceIConcurrentBagEntry{
intSTATE_NOT_IN_USE=0;
intSTATE_IN_USE=1;
intSTATE_REMOVED=-1;
intSTATE_RESERVED=-2;

booleancompareAndSet(intexpectState,intnewState);
voidsetState(intnewState);
intgetState();
}

有了這些數(shù)據(jù)結(jié)構(gòu)的支持,我們的ConcurrentBag就可以實現(xiàn)它光的宣稱了。

基于 Spring Boot + MyBatis Plus + Vue & Element 實現(xiàn)的后臺管理系統(tǒng) + 用戶小程序,支持 RBAC 動態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能

  • 項目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 視頻教程:https://doc.iocoder.cn/video/

獲取連接

連接的獲取是borrow方法,還可以傳入一個timeout作為超時控制。

publicTborrow(longtimeout,finalTimeUnittimeUnit)throwsInterruptedException

首先,如果某個線程執(zhí)行非常快,使用了比較多的連接,就可以使用ThreadLocal的方式快速獲取連接對象,而不用跑到大池子里面去獲取。代碼如下。

//Trythethread-locallistfirst
finalvarlist=threadList.get();
for(inti=list.size()-1;i>=0;i--){
finalvarentry=list.remove(i);
finalTbagEntry=weakThreadLocals?((WeakReference)entry).get():(T)entry;
if(bagEntry!=null&&bagEntry.compareAndSet(STATE_NOT_IN_USE,STATE_IN_USE)){
returnbagEntry;
}
}

我們都知道,包括ArrayList和HashMap一些基礎(chǔ)的結(jié)構(gòu),都是Fail Fast的,如果你在遍歷的時候,刪掉一些數(shù)據(jù),有可能會引起問題。幸運的是,由于我們的List是從ThreadLocal獲取的,它首先就避免了線程安全的問題。

接下來就是遍歷。這段代碼采用的是尾遍歷(頭遍歷會出現(xiàn)錯誤),用于快速的從列表中找到一個可以復(fù)用的對象,然后使用CAS來把狀態(tài)置為使用中。但如果對象正在被使用,則直接刪除它。

在ConcurrentBag里,每個ThreadLocal最多緩存50個連接對象引用。

當(dāng)ThreadLocal里找不到可復(fù)用的對象,它就會到大池子里去拿。也就是下面這段代碼。

//Otherwise,scanthesharedlist...thenpollthehandoffqueue
finalintwaiting=waiters.incrementAndGet();
try{
for(TbagEntry:sharedList){
if(bagEntry.compareAndSet(STATE_NOT_IN_USE,STATE_IN_USE)){
//Ifwemayhavestolenanotherwaiter'sconnection,requestanotherbagadd.
if(waiting>1){
listener.addBagItem(waiting-1);
}
returnbagEntry;
}
}

listener.addBagItem(waiting);

//還拿不到,就需要等待別人釋放了
timeout=timeUnit.toNanos(timeout);
do{
finalvarstart=currentTime();
finalTbagEntry=handoffQueue.poll(timeout,NANOSECONDS);
if(bagEntry==null||bagEntry.compareAndSet(STATE_NOT_IN_USE,STATE_IN_USE)){
returnbagEntry;
}

timeout-=elapsedNanos(start);
}while(timeout>10_000);

returnnull;
}
finally{
waiters.decrementAndGet();
}

首先要注意,這段代碼可能是由不同的線程執(zhí)行的,所以必須要考慮線程安全問題。由于shardList是線程安全的CopyOnWriteArrayList,適合讀多寫少的場景,我們可以直接進行遍歷。

這段代碼的目的是一樣的,需要從sharedList找到一個空閑的連接對象。這里把自增的waiting變量傳遞到外面的代碼進行處理,主要是由于想要根據(jù)waiting的大小來確定是否創(chuàng)建新的對象。

如果無法從池子里獲取連接,則需要等待別的線程釋放一些資源。

創(chuàng)建對象的過程是異步的,要想獲取它,還需要依賴一段循環(huán)代碼。while循環(huán)代碼是納秒精度,會嘗試從handoffQueue里獲取。最終會調(diào)用SynchronousQueue的transfer方法。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實現(xiàn)的后臺管理系統(tǒng) + 用戶小程序,支持 RBAC 動態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能

  • 項目地址:https://github.com/YunaiV/yudao-cloud
  • 視頻教程:https://doc.iocoder.cn/video/

歸還連接

有借就有還,當(dāng)某個連接使用完畢,它將被歸還到池子中。

publicvoidrequite(finalTbagEntry)
{
bagEntry.setState(STATE_NOT_IN_USE);

for(vari=0;waiters.get()>0;i++){
if(bagEntry.getState()!=STATE_NOT_IN_USE||handoffQueue.offer(bagEntry)){
return;
}
elseif((i&0xff)==0xff){
parkNanos(MICROSECONDS.toNanos(10));
}
else{
Thread.yield();
}
}

finalvarthreadLocalList=threadList.get();
if(threadLocalList.size()50){
threadLocalList.add(weakThreadLocals?newWeakReference<>(bagEntry):bagEntry);
}
}

首先,把這個對象置為可用狀態(tài)。然后,代碼會進入一個循環(huán),等待使用方把這個連接接手過去。當(dāng)連接處于STATE_NOT_IN_USE狀態(tài),或者隊列中的數(shù)據(jù)被取走了,那么就可以直接返回了。

由于waiters.get()是實時獲取的,有可能長時間一直大于0,這樣代碼就會變成死循環(huán),浪費CPU。代碼會嘗試不同層次的睡眠,一個是每隔255個waiter睡10ns,一個是使用yield讓出cpu時間片。

如果歸還連接的時候并沒有被其他線程獲取到,那么最后我們會把歸還的連接放入到相對應(yīng)的ThreadLocal里,因為對一個連接來說,借和還,通常是一個線程。

知識點

看起來平平無奇的幾行代碼,為什么搞懂了就能Hold住大部分的并發(fā)編程場景呢?主要還是這里面的知識點太多。下面我簡單羅列一下,你可以逐個攻破。

  1. 使用ThreadLocal來緩存本地資源引用,使用線程封閉的資源來減少鎖的沖突
  2. 采用讀多寫少的線程安全的CopyOnWriteArrayList來緩存所有對象,幾乎不影響讀取效率
  3. 使用基于CAS的AtomicInteger來計算等待者的數(shù)量,無鎖操作使得計算更加快速
  4. 0容量的交換隊列SynchronousQueue,使得對象傳遞更加迅速
  5. 采用compareAndSet的CAS原語來控制狀態(tài)的變更,安全且效率高。很多核心代碼都是這么設(shè)計的
  6. 在循環(huán)中使用park、yield等方法,避免死循環(huán)占用大量CPU
  7. 需要了解并發(fā)數(shù)據(jù)結(jié)構(gòu)中的offer、poll、peek、put、take、add、remove方法的區(qū)別,并靈活應(yīng)用
  8. CAS在設(shè)置狀態(tài)時,采用了volatile關(guān)鍵字修飾,對于volatile的使用也是一個常見的優(yōu)化點
  9. 需要了解WeakReference弱引用在垃圾回收時候的表現(xiàn)

麻雀雖小,五臟俱全。如果你想要你的多線程編程能力更上一層樓,讀一讀這個短小精悍的ConcurrentBag吧。當(dāng)你掌握了它,多線程的那些東西,不過是小菜一碟。

審核編輯 :李倩


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • cpu
    cpu
    +關(guān)注

    關(guān)注

    68

    文章

    10878

    瀏覽量

    212166
  • 數(shù)據(jù)庫
    +關(guān)注

    關(guān)注

    7

    文章

    3822

    瀏覽量

    64506
  • 多線程
    +關(guān)注

    關(guān)注

    0

    文章

    278

    瀏覽量

    20016

原文標(biāo)題:讀懂HikariCP一百行代碼,多線程就是個孫子!

文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    socket 多線程編程實現(xiàn)方法

    是指在同一進程中運行多個線程,每個線程可以獨立執(zhí)行任務(wù)。線程共享進程的資源,如內(nèi)存空間和文件句柄,但每個線程有自己的程序計數(shù)器、寄存器集合
    的頭像 發(fā)表于 11-12 14:16 ?397次閱讀

    Python中多線程和多進程的區(qū)別

    Python作為一種高級編程語言,提供了多種并發(fā)編程的方式,其中多線程與多進程是最常見的兩種方式之一。在本文中,我們將探討Python中多線程與多進程的概念、區(qū)別以及如何使用線程池與進程池來提高并發(fā)執(zhí)行效率。
    的頭像 發(fā)表于 10-23 11:48 ?416次閱讀
    Python中<b class='flag-5'>多線程</b>和多進程的區(qū)別

    ESP32會不會有多線程問題,需要加鎖嗎?

    ESP32會不會有多線程問題,需要加鎖嗎
    發(fā)表于 07-19 08:05

    多線程設(shè)計模式到對 CompletableFuture 的應(yīng)用

    最近在開發(fā) 延保服務(wù) 頻道頁時,為了提高查詢效率,使用到了多線程技術(shù)。為了對多線程方案設(shè)計有更加充分的了解,在業(yè)余時間讀完了《圖解 Java 多線程設(shè)計模式》這本書,覺得收獲良多。本篇文章將介紹其中
    的頭像 發(fā)表于 06-26 14:18 ?370次閱讀
    從<b class='flag-5'>多線程</b>設(shè)計模式到對 CompletableFuture 的應(yīng)用

    bootloader開多線程做引導(dǎo)程序,跳app初始化后直接進hardfualt,為什么?

    如標(biāo)題,想做一遠程升級的項目,bootloader引導(dǎo)區(qū)域和app都是開多線程跑的,就是自己寫了小的任務(wù)調(diào)度器,沒什么功能主要是想讓程序快速的響應(yīng),延時不會對其他程序造成堵塞,程序
    發(fā)表于 04-18 06:07

    鴻蒙OS開發(fā)實例:【ArkTS類庫多線程CPU密集型任務(wù)TaskPool】

    CPU密集型任務(wù)是指需要占用系統(tǒng)資源處理大量計算能力的任務(wù),需要長時間運行,這段時間會阻塞線程其它事件的處理,不適宜放在主線程進行。例如圖像處理、視頻編碼、數(shù)據(jù)分析等。 基于多線程并發(fā)機制處理CPU密集型任務(wù)可以提高CPU
    的頭像 發(fā)表于 04-01 22:25 ?858次閱讀
    鴻蒙OS開發(fā)實例:【ArkTS類庫<b class='flag-5'>多線程</b>CPU密集型任務(wù)TaskPool】

    鴻蒙APP開發(fā):【ArkTS類庫多線程】TaskPool和Worker的對比(2)

    創(chuàng)建Worker的線程稱為宿主線程(不一定是主線程,工作線程也支持創(chuàng)建Worker子線程),Worker自身的
    的頭像 發(fā)表于 03-27 15:44 ?547次閱讀
    鴻蒙APP開發(fā):【ArkTS類庫<b class='flag-5'>多線程</b>】TaskPool和Worker的對比(2)

    鴻蒙APP開發(fā):【ArkTS類庫多線程】TaskPool和Worker的對比

    TaskPool(任務(wù)池)和Worker的作用是為應(yīng)用程序提供一多線程的運行環(huán)境,用于處理耗時的計算任務(wù)或其他密集型任務(wù)。可以有效地避免這些任務(wù)阻塞主線程,從而最大化系統(tǒng)的利用率,降低整體資源消耗,并提高系統(tǒng)的整體性能。
    的頭像 發(fā)表于 03-26 22:09 ?661次閱讀
    鴻蒙APP開發(fā):【ArkTS類庫<b class='flag-5'>多線程</b>】TaskPool和Worker的對比

    java實現(xiàn)多線程的幾種方式

    Java實現(xiàn)多線程的幾種方式 多線程是指程序中包含了兩或以上的線程,每個線程都可以并行執(zhí)行不同的任務(wù)或操作。Java中的
    的頭像 發(fā)表于 03-14 16:55 ?738次閱讀

    python中5種線程鎖盤點

    線程安全是多線程或多進程編程中的一概念,在擁有共享數(shù)據(jù)的多條線程并行執(zhí)行的程序中,線程安全的代碼
    發(fā)表于 03-07 11:08 ?1619次閱讀
    python中5種<b class='flag-5'>線程</b>鎖盤點

    AT socket可以多線程調(diào)用嗎?

    請問AT socket 可以多線程調(diào)用嗎? 有互鎖機制嗎,還是要自己做互鎖。
    發(fā)表于 03-01 08:22

    什么是多核多線程?多核多線程如何提高程序的運行效率?

    線程無法充分利用多核處理器的并行計算能力。
    的頭像 發(fā)表于 02-20 10:22 ?1397次閱讀

    單核多線程的意義是什么?

    一切開始的前提是,你需要知道,CPU執(zhí)行的所有代碼其實就是一條條指令。
    的頭像 發(fā)表于 02-20 09:20 ?1022次閱讀

    linux多線程編程實例

    linux線程
    的頭像 發(fā)表于 02-15 21:16 ?484次閱讀
    linux<b class='flag-5'>多線程</b>編程實例

    Redis7單線程多線程詳解

    主要是指Redis的網(wǎng)絡(luò)IO和鍵值對讀寫是由一線程來完成的。
    的頭像 發(fā)表于 01-16 17:33 ?1872次閱讀
    Redis7單<b class='flag-5'>線程</b>與<b class='flag-5'>多線程</b>詳解
    主站蜘蛛池模板: 毛片网站在线| 免费国产黄网站在线观看视频| 男男gay污小黄文| 男男浪荡性受高hnp肉| 禁漫画羞羞动漫入口| 中日韩毛片| 亚洲国产综合人成综合网站00| 亚洲成人激情电影| 四虎网站在线播放| 国产va免费精品高清在线观看| 男人的天堂在线精品视频| 久久大香线蕉综合爱| 丁香婷婷电影| 天天添| 国产片在线观看狂喷潮bt天堂| 欧美精品高清在线xxxx| 美女扒开尿口给男人桶| 五月在线观看| 女人张开腿男人猛桶视频| 国模沟沟一区二区三区| 午夜视频在线观看免费高清| 女人成午夜大片7777在线| 性欧美高清极品xx| 老师啊灬啊灬用力啊快224视频| 亚洲jizzjizz中文在线播放| 三级三级三级网站网址| 精品国产欧美一区二区最新| 亚洲午夜精品久久久久久成年| 日本加勒比在线播放| 亚洲成成品网站有线| 国产毛片久久国产| 色多多成视频人在线观看| 狠狠综合| 日日干夜夜骑| 一区二区不卡视频| 午夜两性色视频免费网站| 免费爱爱网| 亚洲久久在线| 1000rt人体1000欧美| 综合99| 欧美午夜视频在线观看|