91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

concurrenthashmap中size方法原理的示例分析

發布時間:2022-02-28 14:46:56 來源:億速云 閱讀:169 作者:小新 欄目:開發技術

小編給大家分享一下concurrenthashmap中size方法原理的示例分析,希望大家閱讀完這篇文章之后都有所收獲,下面讓我們一起去探討吧!

concurrenthashmap的size方法原理

同上,這也是同一個面試的時候別人問的,我只是記得看過,在concurrenthashmap中會統計多次,當時就說會統計兩次進行比較,人家接著問為啥。。。我傻了一下,這不是明擺著兩次統計的中間有新的變化了,會導致統計不準確嗎?當時也不知道說啥好,以為他有新的點,就說不知道。面試時很多問題其實冷靜下來想一下,可以更進一步的,有時候其實也是怕他更進一步后下面的挖坑挖大了。

下面具體說一下這個size方法

代碼就不貼了。只說原理。

眾所周知,concurrenthashmap有很多歌segments,首先遍歷segments將每個segment的count加起來作為整個concurrenthashMap的size。如果沒有并發的情況下這自然就可以了,但這是多線程的,如果前腳統計完后腳有變化了,這就不準確了,源碼中引入了,modCount和兩次比較來實現size的確認。具體過程是:

1.進行第一遍遍歷segments數組

將每個segemnt的count加起來作為總數,期間把每個segment的modCount加起來sum作為結果是否被修改的判斷依據。

這里需要提一下modCount,這個是當segment有任何操作都會進行一次增量操作,代表的是對Segment中元素的數量造成影響的操作的次數,這個值只增不減!!!!只增不減很重要,這樣就不會出現一個segment+1,導致modcount+1,而另一個segment-1,即modcount-1 ,從而在統計所有的時候modcount沒有變化。

2.size操作就是遍歷了兩次所有的Segments

每次記錄Segment的modCount值,然后將兩次的modCount進行比較,如果相同,則表示期間沒有發生過寫入操作,就將原先遍歷的結果返回,如果不相同,則把這個過程再重復做一次,如果再不相同,則就需要將所有的Segment都鎖住,然后一個一個遍歷了。

3.如果經判斷發現兩次統計出的modCount并不一致

那就如上所說,要重新啟用全部segment加鎖的方式來進行count的獲取和統計了,這樣在此期間每個segement都被鎖住,無法進行其他操作,統計出的count自然很準確。

而之所以之所以要先不加鎖進行判斷,道理很明顯,就是不希望因為size操作獲取這么多鎖,因為獲取鎖不光占用資源,也會影響其他線程對ConcurrentHash的使用,影響并發情況下程序執行的效率。使用鎖要謹慎!

原理大概就是這樣的,具體的代碼可以去看源碼,而且源碼1.7和1.8有差別。。。有空再貼出來比較比較吧。

concurrenthashmap的size的思考

ConcurrentHashMap是通過分段鎖來控制整個Map的安全性和并發性,那么ConcurrentHashMap在求size的時候是如何兼顧到性能以及安全性的呢?

我們首先會想到以下兩種方法

1.獲取所有的Segment鎖。

這個方法是可行的,但是這會導致并發性能變差,因為你獲取了所有的鎖,那么別的線程將無法對該HashMap執行任何操作。

2.逐個地獲取Segment。

這種方法也有問題,有可能在后面獲取下一個Segment里面的元素的個數的時候,上面一個Segment里面元素的個數已經很可能改變了,因此最后累加到最后,有可能數據是錯誤的。

那么ConcurrentHashMap采用的是什么措施呢。源碼如下所示:

java1.7以前的源碼:

由于在累加count的操作的過程中之前累加過的count發生變化的幾率非常小,所以ConcurrentHashMap先嘗試2次不鎖住Segment的方式來統計每個Segment的大小,如果在統計的過程中Segment的count發生了變化,這時候再加鎖統計Segment的count。

java1.7以及1,7以后的源碼:

取size的核心是sumCount函數。

    final long sumCount() {
        CounterCell[] as = counterCells; CounterCell a;
        long sum = baseCount;
        if (as != null) {
            for (int i = 0; i < as.length; ++i) {
                if ((a = as[i]) != null)
                    sum += a.value;
            }
        }
        return sum;
    }

核心邏輯:當 counterCells 不是 null,就遍歷元素,并和 baseCount 累加。

查看兩個屬性:baseCount 和 counterCells。

先看 baseCount

private transient volatile long baseCount;

baseCount是一個 volatile 的變量,在 addCount 方法中會使用它,而 addCount 方法在 put 結束后會調用。在 addCount 方法中,會對這個變量做 CAS 加法。

  private final void addCount(long x, int check) {
        CounterCell[] as; long b, s;
        if ((as = counterCells) != null ||
            !U.compareAndSetLong(this, BASECOUNT, b = baseCount, s = b + x)) {
            CounterCell a; long v; int m;
            boolean uncontended = true;
            if (as == null || (m = as.length - 1) < 0 ||
                (a = as[ThreadLocalRandom.getProbe() & m]) == null ||
                !(uncontended =
                  U.compareAndSetLong(a, CELLVALUE, v = a.value, v + x))) {
                fullAddCount(x, uncontended);
                return;
            }
            if (check <= 1)
                return;
            s = sumCount();
        }

但是如果并發導致 CAS 失敗了,怎么辦呢?使用 counterCells。

如果上面 CAS 失敗了,在 fullAddCount 方法中,會繼續死循環操作,直到成功。

最后,再來看一下counterCells這個類。

    @jdk.internal.vm.annotation.Contended static final class CounterCell {
        volatile long value;
        CounterCell(long x) { value = x; }
    }

上述源碼中的注釋是為了避免偽共享(false sharing)。

先引用個偽共享的解釋: 緩存系統中是以緩存行(cache line)為單位存儲的。

緩存行是2的整數冪個連續字節, 一般為32-256個字節。最常見的緩存行大小是64個字節。

當多線程修改互相獨立的變量時, 如果這些變量共享同一個緩存行,就會無意中影響彼此的性能,這就是偽共享。

看完了這篇文章,相信你對“concurrenthashmap中size方法原理的示例分析”有了一定的了解,如果想了解更多相關知識,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

科技| 望城县| 佛学| 建湖县| 永善县| 改则县| 兰坪| 荥经县| 普洱| 亚东县| 福泉市| 辉县市| 和林格尔县| 贵南县| 徐汇区| 青铜峡市| 梁平县| 枣强县| 宾阳县| 藁城市| 双牌县| 文化| 吉安市| 旺苍县| 呈贡县| 股票| 麦盖提县| 庆元县| 高邮市| SHOW| 庄浪县| 望谟县| 平罗县| 灵武市| 当雄县| 祁东县| 浑源县| 呼图壁县| 丰顺县| 桦川县| 黔西县|