1. <ul id="0c1fb"></ul>

      <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
      <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区

      RELATEED CONSULTING
      相關(guān)咨詢(xún)
      選擇下列產(chǎn)品馬上在線(xiàn)溝通
      服務(wù)時(shí)間:8:30-17:00
      你可能遇到了下面的問(wèn)題
      關(guān)閉右側(cè)工具欄

      新聞中心

      這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
      死磕java集合之ConcurrentHashMap源碼分析(一)-創(chuàng)新互聯(lián)

      前記,從這篇文章開(kāi)始我們換一種學(xué)習(xí)的方式,彤哥先拋出問(wèn)題,大家嘗試著在腦海中回答這些問(wèn)題,然后再進(jìn)入我們的源碼分析過(guò)程,最后彤哥再挑幾個(gè)問(wèn)題回答。

      10多年專(zhuān)注成都網(wǎng)站制作,成都定制網(wǎng)頁(yè)設(shè)計(jì),個(gè)人網(wǎng)站制作服務(wù),為大家分享網(wǎng)站制作知識(shí)、方案,網(wǎng)站設(shè)計(jì)流程、步驟,成功服務(wù)上千家企業(yè)。為您提供網(wǎng)站建設(shè),網(wǎng)站制作,網(wǎng)頁(yè)設(shè)計(jì)及定制高端網(wǎng)站建設(shè)服務(wù),專(zhuān)注于成都定制網(wǎng)頁(yè)設(shè)計(jì),高端網(wǎng)頁(yè)制作,對(duì)酒樓設(shè)計(jì)等多個(gè)領(lǐng)域,擁有多年的網(wǎng)站設(shè)計(jì)經(jīng)驗(yàn)。

      開(kāi)篇問(wèn)題

      (1)ConcurrentHashMap與HashMap的數(shù)據(jù)結(jié)構(gòu)是否一樣?

      (2)HashMap在多線(xiàn)程環(huán)境下何時(shí)會(huì)出現(xiàn)并發(fā)安全問(wèn)題?

      (3)ConcurrentHashMap是怎么解決并發(fā)安全問(wèn)題的?

      (4)ConcurrentHashMap使用了哪些鎖?

      (5)ConcurrentHashMap的擴(kuò)容是怎么進(jìn)行的?

      (6)ConcurrentHashMap是否是強(qiáng)一致性的?

      (7)ConcurrentHashMap不能解決哪些問(wèn)題?

      (8)ConcurrentHashMap中有哪些不常見(jiàn)的技術(shù)值得學(xué)習(xí)?

      簡(jiǎn)介

      ConcurrentHashMap是HashMap的線(xiàn)程安全版本,內(nèi)部也是使用(數(shù)組 + 鏈表 + 紅黑樹(shù))的結(jié)構(gòu)來(lái)存儲(chǔ)元素。

      相比于同樣線(xiàn)程安全的HashTable來(lái)說(shuō),效率等各方面都有極大地提高。

      各種鎖簡(jiǎn)介

      這里先簡(jiǎn)單介紹一下各種鎖,以便下文講到相關(guān)概念時(shí)能有個(gè)印象。

      (1)synchronized

      java中的關(guān)鍵字,內(nèi)部實(shí)現(xiàn)為監(jiān)視器鎖,主要是通過(guò)對(duì)象監(jiān)視器在對(duì)象頭中的字段來(lái)表明的。

      synchronized從舊版本到現(xiàn)在已經(jīng)做了很多優(yōu)化了,在運(yùn)行時(shí)會(huì)有三種存在方式:偏向鎖,輕量級(jí)鎖,重量級(jí)鎖。

      偏向鎖,是指一段同步代碼一直被一個(gè)線(xiàn)程訪(fǎng)問(wèn),那么這個(gè)線(xiàn)程會(huì)自動(dòng)獲取鎖,降低獲取鎖的代價(jià)。

      輕量級(jí)鎖,是指當(dāng)鎖是偏向鎖時(shí),被另一個(gè)線(xiàn)程所訪(fǎng)問(wèn),偏向鎖會(huì)升級(jí)為輕量級(jí)鎖,這個(gè)線(xiàn)程會(huì)通過(guò)自旋的方式嘗試獲取鎖,不會(huì)阻塞,提高性能。

      重量級(jí)鎖,是指當(dāng)鎖是輕量級(jí)鎖時(shí),當(dāng)自旋的線(xiàn)程自旋了一定的次數(shù)后,還沒(méi)有獲取到鎖,就會(huì)進(jìn)入阻塞狀態(tài),該鎖升級(jí)為重量級(jí)鎖,重量級(jí)鎖會(huì)使其他線(xiàn)程阻塞,性能降低。

      (2)CAS

      CAS,Compare And Swap,它是一種樂(lè)觀(guān)鎖,認(rèn)為對(duì)于同一個(gè)數(shù)據(jù)的并發(fā)操作不一定會(huì)發(fā)生修改,在更新數(shù)據(jù)的時(shí)候,嘗試去更新數(shù)據(jù),如果失敗就不斷嘗試。

      (3)volatile(非鎖)

      java中的關(guān)鍵字,當(dāng)多個(gè)線(xiàn)程訪(fǎng)問(wèn)同一個(gè)變量時(shí),一個(gè)線(xiàn)程修改了這個(gè)變量的值,其他線(xiàn)程能夠立即看得到修改的值。(這里牽涉到j(luò)ava內(nèi)存模型的知識(shí),感興趣的同學(xué)可以自己查查相關(guān)資料)

      volatile只保證可見(jiàn)性,不保證原子性,比如 volatile修改的變量 i,針對(duì)i++操作,不保證每次結(jié)果都正確,因?yàn)閕++操作是兩步操作,相當(dāng)于 i = i +1,先讀取,再加1,這種情況volatile是無(wú)法保證的。

      (4)自旋鎖

      自旋鎖,是指嘗試獲取鎖的線(xiàn)程不會(huì)阻塞,而是循環(huán)的方式不斷嘗試,這樣的好處是減少線(xiàn)程的上下文切換帶來(lái)的開(kāi)鎖,提高性能,缺點(diǎn)是循環(huán)會(huì)消耗CPU。

      (5)分段鎖

      分段鎖,是一種鎖的設(shè)計(jì)思路,它細(xì)化了鎖的粒度,主要運(yùn)用在ConcurrentHashMap中,實(shí)現(xiàn)高效的并發(fā)操作,當(dāng)操作不需要更新整個(gè)數(shù)組時(shí),就只鎖數(shù)組中的一項(xiàng)就可以了。

      (5)ReentrantLock

      可重入鎖,是指一個(gè)線(xiàn)程獲取鎖之后再?lài)L試獲取鎖時(shí)會(huì)自動(dòng)獲取鎖,可重入鎖的優(yōu)點(diǎn)是避免死鎖。

      其實(shí),synchronized也是可重入鎖。

      源碼分析

      構(gòu)造方法

      public ConcurrentHashMap() {
      }
      
      public ConcurrentHashMap(int initialCapacity) {
          if (initialCapacity < 0)
              throw new IllegalArgumentException();
          int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
                  MAXIMUM_CAPACITY :
                  tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
          this.sizeCtl = cap;
      }
      
      public ConcurrentHashMap(Map m) {
          this.sizeCtl = DEFAULT_CAPACITY;
          putAll(m);
      }
      
      public ConcurrentHashMap(int initialCapacity, float loadFactor) {
          this(initialCapacity, loadFactor, 1);
      }
      
      public ConcurrentHashMap(int initialCapacity,
                               float loadFactor, int concurrencyLevel) {
          if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0)
              throw new IllegalArgumentException();
          if (initialCapacity < concurrencyLevel)   // Use at least as many bins
              initialCapacity = concurrencyLevel;   // as estimated threads
          long size = (long)(1.0 + (long)initialCapacity / loadFactor);
          int cap = (size >= (long)MAXIMUM_CAPACITY) ?
                  MAXIMUM_CAPACITY : tableSizeFor((int)size);
          this.sizeCtl = cap;
      }

      構(gòu)造方法與HashMap對(duì)比可以發(fā)現(xiàn),沒(méi)有了HashMap中的threshold和loadFactor,而是改用了sizeCtl來(lái)控制,而且只存儲(chǔ)了容量在里面,那么它是怎么用的呢?官方給出的解釋如下:

      (1)-1,表示有線(xiàn)程正在進(jìn)行初始化操作

      (2)-(1 + nThreads),表示有n個(gè)線(xiàn)程正在一起擴(kuò)容

      (3)0,默認(rèn)值,后續(xù)在真正初始化的時(shí)候使用默認(rèn)容量

      (4)> 0,初始化或擴(kuò)容完成后下一次的擴(kuò)容門(mén)檻

      至于,官方這個(gè)解釋對(duì)不對(duì)我們后面再討論。

      添加元素

      public V put(K key, V value) {
          return putVal(key, value, false);
      }
      
      final V putVal(K key, V value, boolean onlyIfAbsent) {
          // key和value都不能為null
          if (key == null || value == null) throw new NullPointerException();
          // 計(jì)算hash值
          int hash = spread(key.hashCode());
          // 要插入的元素所在桶的元素個(gè)數(shù)
          int binCount = 0;
          // 死循環(huán),結(jié)合CAS使用(如果CAS失敗,則會(huì)重新取整個(gè)桶進(jìn)行下面的流程)
          for (Node[] tab = table;;) {
              Node f; int n, i, fh;
              if (tab == null || (n = tab.length) == 0)
                  // 如果桶未初始化或者桶個(gè)數(shù)為0,則初始化桶
                  tab = initTable();
              else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
                  // 如果要插入的元素所在的桶還沒(méi)有元素,則把這個(gè)元素插入到這個(gè)桶中
                  if (casTabAt(tab, i, null,
                          new Node(hash, key, value, null)))
                      // 如果使用CAS插入元素時(shí),發(fā)現(xiàn)已經(jīng)有元素了,則進(jìn)入下一次循環(huán),重新操作
                      // 如果使用CAS插入元素成功,則break跳出循環(huán),流程結(jié)束
                      break;                   // no lock when adding to empty bin
              }
              else if ((fh = f.hash) == MOVED)
                  // 如果要插入的元素所在的桶的第一個(gè)元素的hash是MOVED,則當(dāng)前線(xiàn)程幫忙一起遷移元素
                  tab = helpTransfer(tab, f);
              else {
                  // 如果這個(gè)桶不為空且不在遷移元素,則鎖住這個(gè)桶(分段鎖)
                  // 并查找要插入的元素是否在這個(gè)桶中
                  // 存在,則替換值(onlyIfAbsent=false)
                  // 不存在,則插入到鏈表結(jié)尾或插入樹(shù)中
                  V oldVal = null;
                  synchronized (f) {
                      // 再次檢測(cè)第一個(gè)元素是否有變化,如果有變化則進(jìn)入下一次循環(huán),從頭來(lái)過(guò)
                      if (tabAt(tab, i) == f) {
                          // 如果第一個(gè)元素的hash值大于等于0(說(shuō)明不是在遷移,也不是樹(shù))
                          // 那就是桶中的元素使用的是鏈表方式存儲(chǔ)
                          if (fh >= 0) {
                              // 桶中元素個(gè)數(shù)賦值為1
                              binCount = 1;
                              // 遍歷整個(gè)桶,每次結(jié)束binCount加1
                              for (Node e = f;; ++binCount) {
                                  K ek;
                                  if (e.hash == hash &&
                                          ((ek = e.key) == key ||
                                                  (ek != null && key.equals(ek)))) {
                                      // 如果找到了這個(gè)元素,則賦值了新值(onlyIfAbsent=false)
                                      // 并退出循環(huán)
                                      oldVal = e.val;
                                      if (!onlyIfAbsent)
                                          e.val = value;
                                      break;
                                  }
                                  Node pred = e;
                                  if ((e = e.next) == null) {
                                      // 如果到鏈表尾部還沒(méi)有找到元素
                                      // 就把它插入到鏈表結(jié)尾并退出循環(huán)
                                      pred.next = new Node(hash, key,
                                              value, null);
                                      break;
                                  }
                              }
                          }
                          else if (f instanceof TreeBin) {
                              // 如果第一個(gè)元素是樹(shù)節(jié)點(diǎn)
                              Node p;
                              // 桶中元素個(gè)數(shù)賦值為2
                              binCount = 2;
                              // 調(diào)用紅黑樹(shù)的插入方法插入元素
                              // 如果成功插入則返回null
                              // 否則返回尋找到的節(jié)點(diǎn)
                              if ((p = ((TreeBin)f).putTreeVal(hash, key,
                                      value)) != null) {
                                  // 如果找到了這個(gè)元素,則賦值了新值(onlyIfAbsent=false)
                                  // 并退出循環(huán)
                                  oldVal = p.val;
                                  if (!onlyIfAbsent)
                                      p.val = value;
                              }
                          }
                      }
                  }
                  // 如果binCount不為0,說(shuō)明成功插入了元素或者尋找到了元素
                  if (binCount != 0) {
                      // 如果鏈表元素個(gè)數(shù)達(dá)到了8,則嘗試樹(shù)化
                      // 因?yàn)樯厦姘言夭迦氲綐?shù)中時(shí),binCount只賦值了2,并沒(méi)有計(jì)算整個(gè)樹(shù)中元素的個(gè)數(shù)
                      // 所以不會(huì)重復(fù)樹(shù)化
                      if (binCount >= TREEIFY_THRESHOLD)
                          treeifyBin(tab, i);
                      // 如果要插入的元素已經(jīng)存在,則返回舊值
                      if (oldVal != null)
                          return oldVal;
                      // 退出外層大循環(huán),流程結(jié)束
                      break;
                  }
              }
              }
              // 成功插入元素,元素個(gè)數(shù)加1(是否要擴(kuò)容在這個(gè)里面)
              addCount(1L, binCount);
              // 成功插入元素返回null
              return null;
          }

      整體流程跟HashMap比較類(lèi)似,大致是以下幾步:

      (1)如果桶數(shù)組未初始化,則初始化;

      (2)如果待插入的元素所在的桶為空,則嘗試把此元素直接插入到桶的第一個(gè)位置;

      (3)如果正在擴(kuò)容,則當(dāng)前線(xiàn)程一起加入到擴(kuò)容的過(guò)程中;

      (4)如果待插入的元素所在的桶不為空且不在遷移元素,則鎖住這個(gè)桶(分段鎖);

      (5)如果當(dāng)前桶中元素以鏈表方式存儲(chǔ),則在鏈表中尋找該元素或者插入元素;

      (6)如果當(dāng)前桶中元素以紅黑樹(shù)方式存儲(chǔ),則在紅黑樹(shù)中尋找該元素或者插入元素;

      (7)如果元素存在,則返回舊值;

      (8)如果元素不存在,整個(gè)Map的元素個(gè)數(shù)加1,并檢查是否需要擴(kuò)容;

      添加元素操作中使用的鎖主要有(自旋鎖 + CAS + synchronized + 分段鎖)。

      為什么使用synchronized而不是ReentrantLock?

      因?yàn)閟ynchronized已經(jīng)得到了極大地優(yōu)化,在特定情況下并不比ReentrantLock差。


      未完待續(xù)~~


      歡迎關(guān)注我的公眾號(hào)“彤哥讀源碼”,查看更多源碼系列文章, 與彤哥一起暢游源碼的海洋。

      死磕 java集合之ConcurrentHashMap源碼分析(一)

      另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線(xiàn),公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性?xún)r(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專(zhuān)為企業(yè)上云打造定制,能夠滿(mǎn)足用戶(hù)豐富、多元化的應(yīng)用場(chǎng)景需求。


      網(wǎng)頁(yè)題目:死磕java集合之ConcurrentHashMap源碼分析(一)-創(chuàng)新互聯(lián)
      路徑分享:http://www.ef60e0e.cn/article/dgjche.html
      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区
      1. <ul id="0c1fb"></ul>

        <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
        <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

        遵义县| 南皮县| 郸城县| 宜宾市| 明溪县| 延安市| 卢湾区| 横山县| 常山县| 石林| 鸡泽县| 无极县| 怀来县| 锡林郭勒盟| 文水县| 南投县| 竹溪县| 海原县| 中方县| 宣恩县| 台湾省| 岳阳市| 屏山县| 南岸区| 乐业县| 凤翔县| 和林格尔县| 山阴县| 新蔡县| 临夏县| 胶南市| 万荣县| 巴楚县| 海淀区| 太白县| 雷山县| 陈巴尔虎旗| 沁水县| 凤翔县| 通化市| 炉霍县|