關(guān)于 ConcurrentHashMap 1.8 中的線程探針哈希

ConcurrentHashMap 在累加鍵值對個數(shù)的 addCount 函數(shù)中弟胀,使用 ThreadLocalRandom.getProbe() 得到線程的探針哈希值。

在這里,這個探針哈希值的作用是哈希線程呐萨,將線程和數(shù)組中的不用元素對應(yīng)起來帜羊,盡量避免線程爭用同一數(shù)組元素市袖。探針哈希值和 map 里使用的哈希值的區(qū)別是谴分,當(dāng)線程發(fā)生數(shù)組元素爭用后锈麸,可以改變線程的探針哈希值,讓線程去使用另一個數(shù)組元素牺蹄,而 map 中 key 對象的哈希值忘伞,由于有定位 value 的需求,所以它是一定不能變的沙兰。

那么這個探針哈希值是在哪計算的呢氓奈?帶著這個問題我們繼續(xù)往下看。
ThreadLocalRandom.getProbe() 方法如下:

    /**
     * Returns the probe value for the current thread without forcing
     * initialization. Note that invoking ThreadLocalRandom.current()
     * can be used to force initialization on zero return.
     */
    static final int getProbe() {
        return UNSAFE.getInt(Thread.currentThread(), PROBE);
    }

PROBE 是什么僧凰?

    // Unsafe mechanics
    private static final sun.misc.Unsafe UNSAFE;
    ...
    private static final long PROBE;
    ...
    static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> tk = Thread.class;
            ...
            PROBE = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("threadLocalRandomProbe"));
            ...
        } catch (Exception e) {
            throw new Error(e);
        }
    }  

可以看到 PROBE 表示的是 Thread 類 threadLocalRandomProbe 字段的偏移量探颈。
所以 getProbe 方法的功能就是簡單的返回當(dāng)前線程 threadLocalRandomProbe 字段的值。

接著去 Thread 類看看這個 threadLocalRandomProbe 字段训措,

    /** Probe hash value; nonzero if threadLocalRandomSeed initialized */
    @sun.misc.Contended("tlr")
    int threadLocalRandomProbe;

Thread 類僅僅是定義了這個字段伪节,并沒有將其初始化,其初始化工作由 ThreadLocalRandom 類來做绩鸣。
ThreadLocalRandom 類的 localInit 方法完成初始化工作怀大,

    /**
     * Initialize Thread fields for the current thread.  Called only
     * when Thread.threadLocalRandomProbe is zero, indicating that a
     * thread local seed value needs to be generated. Note that even
     * though the initialization is purely thread-local, we need to
     * rely on (static) atomic generators to initialize the values.
     */
    static final void localInit() {
        // probeGenerator 是一個 AtomicInteger 類型
        // PROBE_INCREMENT 是一個靜態(tài)常量,值為 0x9e3779b9
        int p = probeGenerator.addAndGet(PROBE_INCREMENT);
        int probe = (p == 0) ? 1 : p; // skip 0
        long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));
        Thread t = Thread.currentThread(); // 獲取當(dāng)前線程
        // 通過 Unsafe 對象初始化當(dāng)前線程的 threadLocalRandomSeed 字段
        UNSAFE.putLong(t, SEED, seed);
        // 通過 Unsafe 對象初始化當(dāng)前線程的 threadLocalRandomProbe 字段
        UNSAFE.putInt(t, PROBE, probe);
    }

SEED 和 PROBE 類似呀闻,它表示的是 Thread 類 threadLocalRandomSeed 字段的偏移量化借。

在 ThreadLocalRandom 類的這個 localInit 方法里,同時初始化了當(dāng)前線程的 threadLocalRandomSeed 字段和 threadLocalRandomProbe 字段捡多。

所以在 Thread 類 threadLocalRandomProbe 字段上的注釋中說:nonzero if threadLocalRandomSeed initialized蓖康。就是說如果 threadLocalRandomSeed 字段被初始化了,threadLocalRandomProbe 字段就非零垒手。因為它倆是同時被初始化的蒜焊。

除此之外,也可以通過 ThreadLocalRandom 類的 advanceProbe 方法更改當(dāng)前線程 threadLocalRandomProbe 的值科贬。

    /**
     * Pseudo-randomly advances and records the given probe value for the
     * given thread.
     */
    static final int advanceProbe(int probe) {
        probe ^= probe << 13;   // xorshift
        probe ^= probe >>> 17;
        probe ^= probe << 5;
        UNSAFE.putInt(Thread.currentThread(), PROBE, probe);
        return probe;
    }

ConcurrentHashMap 里的 fullAddCount 方法會調(diào)用 ThreadLocalRandom.localInit() 初始化當(dāng)前線程的探針哈希值泳梆;當(dāng)發(fā)生線程爭用后,也會調(diào)用 ThreadLocalRandom.advanceProbe(h) 更改當(dāng)前線程的探針哈希值榜掌,


    private final void fullAddCount(long x, boolean wasUncontended) {
        int h;
        if ((h = ThreadLocalRandom.getProbe()) == 0) {
            ThreadLocalRandom.localInit();      // force initialization
            h = ThreadLocalRandom.getProbe();
            wasUncontended = true;
        }
        ...
        h = ThreadLocalRandom.advanceProbe(h);
        ...
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末优妙,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子憎账,更是在濱河造成了極大的恐慌套硼,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件胞皱,死亡現(xiàn)場離奇詭異熟菲,居然都是意外死亡看政,警方通過查閱死者的電腦和手機(jī)朴恳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進(jìn)店門抄罕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人于颖,你說我怎么就攤上這事呆贿。” “怎么了森渐?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵做入,是天一觀的道長。 經(jīng)常有香客問我同衣,道長竟块,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任耐齐,我火速辦了婚禮浪秘,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘埠况。我一直安慰自己耸携,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布辕翰。 她就那樣靜靜地躺著夺衍,像睡著了一般。 火紅的嫁衣襯著肌膚如雪喜命。 梳的紋絲不亂的頭發(fā)上沟沙,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天,我揣著相機(jī)與錄音壁榕,去河邊找鬼矛紫。 笑死,一個胖子當(dāng)著我的面吹牛护桦,可吹牛的內(nèi)容都是我干的含衔。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼二庵,長吁一口氣:“原來是場噩夢啊……” “哼贪染!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起催享,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤杭隙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后因妙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體痰憎,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡票髓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了铣耘。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片洽沟。...
    茶點(diǎn)故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蜗细,靈堂內(nèi)的尸體忽然破棺而出裆操,到底是詐尸還是另有隱情,我是刑警寧澤炉媒,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布踪区,位于F島的核電站,受9級特大地震影響吊骤,放射性物質(zhì)發(fā)生泄漏缎岗。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一白粉、第九天 我趴在偏房一處隱蔽的房頂上張望传泊。 院中可真熱鬧,春花似錦蜗元、人聲如沸或渤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽薪鹦。三九已至,卻和暖如春惯豆,著一層夾襖步出監(jiān)牢的瞬間池磁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工楷兽, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留地熄,地道東北人。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓芯杀,卻偏偏與公主長得像端考,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子揭厚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評論 2 349