最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

解析HikariCP一百行代碼輕松掌握多線程

 更新時間:2022年09月29日 08:36:14   作者:小姐姐味道  
這篇文章主要為大家介紹了HikariCP一百行代碼解析,輕松掌握多線程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

引言

Java屆很難得有讀百十行代碼就能增加修煉的機會,這里有一個。

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

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

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

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

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

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

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

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

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

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

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

public interface IConcurrentBagEntry{
    int STATE_NOT_IN_USE = 0;
    int STATE_IN_USE = 1;
    int STATE_REMOVED = -1;
    int STATE_RESERVED = -2;
    boolean compareAndSet(int expectState, int newState);
    void setState(int newState);
    int getState();
}

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

獲取連接

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

public T borrow(long timeout, final TimeUnit timeUnit) throws InterruptedException

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

// Try the thread-local list first
final var list = threadList.get();
for (int i = list.size() - 1; i >= 0; i--) {
    final var entry = list.remove(i);
    final T bagEntry = weakThreadLocals ? ((WeakReference<T>) entry).get() : (T) entry;
    if (bagEntry != null && bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
        return bagEntry;
    }
}

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

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

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

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

// Otherwise, scan the shared list ... then poll the handoff queue
final int waiting = waiters.incrementAndGet();
try {
   for (T bagEntry : sharedList) {
      if (bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
         // If we may have stolen another waiter's connection, request another bag add.
         if (waiting > 1) {
            listener.addBagItem(waiting - 1);
         }
         return bagEntry;
      }
   }
   listener.addBagItem(waiting);
   // 還拿不到,就需要等待別人釋放了
   timeout = timeUnit.toNanos(timeout);
   do {
      final var start = currentTime();
      final T bagEntry = handoffQueue.poll(timeout, NANOSECONDS);
      if (bagEntry == null || bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
         return bagEntry;
      }
      timeout -= elapsedNanos(start);
   } while (timeout > 10_000);
   return null;
}
finally {
   waiters.decrementAndGet();
}

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

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

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

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

歸還連接

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

public void requite(final T bagEntry)
{
   bagEntry.setState(STATE_NOT_IN_USE);
   for (var i = 0; waiters.get() > 0; i++) {
      if (bagEntry.getState() != STATE_NOT_IN_USE || handoffQueue.offer(bagEntry)) {
         return;
      }
      else if ((i & 0xff) == 0xff) {
         parkNanos(MICROSECONDS.toNanos(10));
      }
      else {
         Thread.yield();
      }
   }
   final var threadLocalList = threadList.get();
   if (threadLocalList.size() < 50) {
      threadLocalList.add(weakThreadLocals ? new WeakReference<>(bagEntry) : bagEntry);
   }
}

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

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

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

知識點

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

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

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

以上就是解析HikariCP一百行代碼輕松掌握多線程的詳細內(nèi)容,更多關(guān)于HikariCP 多線程的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解Java的Hibernate框架中的Interceptor和Collection

    詳解Java的Hibernate框架中的Interceptor和Collection

    這篇文章主要介紹了Java的Hibernate框架中的Interceptor和Collection,Hibernate是Java的SSH三大web開發(fā)框架之一,需要的朋友可以參考下
    2016-01-01
  • 超好用的Java工具類庫Hutool用法詳解

    超好用的Java工具類庫Hutool用法詳解

    Hutool是一個小而全的Java工具類庫,通過靜態(tài)方法封裝,降低相關(guān)API的學習成本,提高工作效率,下面就跟隨小編一起來學習一下Hutool的具體用法吧
    2023-09-09
  • Java中實現(xiàn)Map按值排序的多種方法

    Java中實現(xiàn)Map按值排序的多種方法

    Java的Map接口默認不保證元素的順序,本文介紹如何使用TreeMap、Collections.sort()結(jié)合Entry、Stream API以及Guava庫等技術(shù)手段實現(xiàn)Map按值排序,每種方法都有其特點,在選擇具體方法時應(yīng)考慮項目的具體需求及個人偏好,需要的朋友可以參考下
    2025-03-03
  • java List去掉重復(fù)元素的幾種方式(小結(jié))

    java List去掉重復(fù)元素的幾種方式(小結(jié))

    這篇文章主要介紹了java List去掉重復(fù)元素的幾種方式,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-06-06
  • 動態(tài)更改Spring定時任務(wù)Cron表達式的優(yōu)雅方案實例詳解

    動態(tài)更改Spring定時任務(wù)Cron表達式的優(yōu)雅方案實例詳解

    spring定時器非常強大,但是有時候我們需要在不需要重啟應(yīng)用就可以動態(tài)的改變Cron表達式的值,下面這篇文章主要給大家介紹了關(guān)于動態(tài)更改Spring定時任務(wù)Cron表達式的優(yōu)雅方案,需要的朋友可以參考下
    2022-12-12
  • Mybatis Plus select 實現(xiàn)只查詢部分字段

    Mybatis Plus select 實現(xiàn)只查詢部分字段

    這篇文章主要介紹了Mybatis Plus select 實現(xiàn)只查詢部分字段的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • 完美解決Java中的線程安全問題

    完美解決Java中的線程安全問題

    下面小編就為大家?guī)硪黄昝澜鉀QJava中的線程安全問題。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-08-08
  • mybatis-plus如何使用mapper的xml

    mybatis-plus如何使用mapper的xml

    這篇文章主要介紹了mybatis-plus如何使用mapper的xml問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • Java8新特性Stream流詳解

    Java8新特性Stream流詳解

    Java8 Stream使用的是函數(shù)式編程模式,如同它的名字一樣,它可以被用來對集合進行鏈狀流式的操作,本文就將帶著你如何使用 Java 8 不同類型的 Stream 操作,同時還將了解流的處理順序,以及不同順序的流操作是如何影響運行時性能的
    2023-07-07
  • 工作中Java集合的規(guī)范使用操作詳解

    工作中Java集合的規(guī)范使用操作詳解

    這篇文章主要為大家詳細介紹了工作中Java集合使用時需要注意的規(guī)范,文中的示例代碼講解詳細,對我們使用Java集合有一定的幫助,感興趣的可以了解一下
    2022-11-11

最新評論

怀安县| 乌兰浩特市| 威信县| 含山县| 合阳县| 淮安市| 密山市| 济源市| 开封县| 嘉善县| 桂阳县| 顺昌县| 黎城县| 青海省| 临海市| 鄂托克旗| 吴江市| 汪清县| 腾冲县| 华容县| 环江| 黔西县| 甘南县| 柳河县| 云阳县| 黑山县| 卫辉市| 廉江市| 县级市| 隆子县| 久治县| 堆龙德庆县| 通山县| 乳山市| 南阳市| 洛阳市| 邹城市| 汉川市| 曲阜市| 镇赉县| 左云县|