1. 程式人生 > >Java8 ThreadLocal 原始碼解析

Java8 ThreadLocal 原始碼解析

前言

ThreadLocal ,像是一個神祕的黑衣人,令人望而生畏。唯有下定決心,一探究竟,方能解開他神祕的面紗、在Android中,Handler,EventBus,ConnectionPool 等等,都曾出現它的身影

是什麼東西?

看到Thread,就想到應該是與執行緒有關吧,其次,Local是說本地,那組合起來就是執行緒私有,就是說每個執行緒都有備份,各備份不是同一個物件,一般來說,他的用途就是讓各個執行緒擁有不用的物件,它的物件都是 new 出來的,哪有人可能會問,既然是 new 出來的,那為什麼還要用 ThreadLocal,直接 new 一個物件不就行了,你如果不仔細瞭解 ThreadLocal,就很難解答這樣的問題

前世今生

是一個泛型類

public class ThreadLocal<T> {}

原始碼解析

屬性

nextHashCode 是獲取 hashcode 值,HASH_INCREMENT 初始值為0x61c88647,nextHashCode()函式每次呼叫增加增量獲取下一個值,大家可以好好看看Atomic的實現,以及底層Unsafe的語義

    private final int threadLocalHashCode = nextHashCode();

    /**
     * The next hash code to be given out. Updated atomically. Starts at
     * zero.
     */
private static AtomicInteger nextHashCode = new AtomicInteger(); /** * The difference between successively generated hash codes - turns * implicit sequential thread-local IDs into near-optimally spread * multiplicative hash values for power-of-two-sized tables. */
private static final int HASH_INCREMENT = 0x61c88647; /** * Returns the next hash code. */ private static int nextHashCode() { return nextHashCode.getAndAdd(HASH_INCREMENT); }

函式

初始值函式

    protected T initialValue() {
        return null;
    }

set ,將值拷貝到當前執行緒,使用ThreadLocalMap,這是一個內部類,要了解ThreadLocal,這個類是不得不瞭解的

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

一探ThreadLocalMap,前方高能,他是一個靜態內部類,類似於WeakHashMap,只不過他沒有WeakHashMap那麼強大,沒有Map那麼多的介面,不過作為一個內部類,只需要為ThreadLocal服務就可以了,這一條可以在EffectiveJava裡找到,廢話不多說,看一下他的Entry,繼承自WeakReference,關於WeakReference,又可以花大篇幅來介紹了,不過現在暫時不說了,你只要記住當發生GC的時候,WeakReference的物件都會被回收。同樣內部也是用Entry陣列,預設大小為16,set 函式需要注意的是如何處理雜湊碰撞的問題,這裡扯一下,處理雜湊碰撞的如HashMap使用了拉鍊法,而這裡使用了簡單的開發地址法,具體來說就是如果發生了碰撞,就位置就+1,一直到有位置。

static class ThreadLocalMap {

        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        /**
         * The initial capacity -- MUST be a power of two.
         */
        private static final int INITIAL_CAPACITY = 16;

        /**
         * The table, resized as necessary.
         * table.length MUST always be a power of two.
         */
        private Entry[] table;

        /**
         * The number of entries in the table.
         */
        private int size = 0;

        /**
         * The next size value at which to resize.
         */
        private int threshold; // Default to 0
        }
        // set 函式,同樣也是使用key的hashcode 與 len-1的
        // 與值
        private void set(ThreadLocal<?> key, Object value) {

            // We don't use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);

            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get();

                if (k == key) {
                    e.value = value;
                    return;
                }

                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

最重要的 get 函式,我們需要記住的是,每個執行緒有一個ThreadLocalMap,每個Map可以放很多個值,也就是不用的ThreadLocal物件。如果Get 不到資料的話,就會呼叫InitialValue函式進行賦值,所以有時候我們會重寫這個函式,以保證get函式都能獲取到正確的值

    /**
     * Returns the value in the current thread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} method.
     *
     * @return the current thread's value of this thread-local
     */
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

由於使用了WeakReference,可能會存在值丟失,那ThreadLocalMap又是怎麼處理這種情況呢?首先自然是處理hash碰撞的問題,通過nextIndex來遍歷,其中如果出現某個key獲取不到的話,就會執行
expungeStaleEntry(i) 刪除舊的entry,具體的做法就是刪除這個entry,以及之後出現為空的所有entry

private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
            Entry[] tab = table;
            int len = tab.length;

            while (e != null) {
                ThreadLocal<?> k = e.get();
                if (k == key)
                    return e;
                if (k == null)
                    expungeStaleEntry(i);
                else
                    i = nextIndex(i, len);
                e = tab[i];
            }
            return null;
        }

        private int expungeStaleEntry(int staleSlot) {
            Entry[] tab = table;
            int len = tab.length;

            // expunge entry at staleSlot
            tab[staleSlot].value = null;
            tab[staleSlot] = null;
            size--;

            // Rehash until we encounter null
            Entry e;
            int i;
            for (i = nextIndex(staleSlot, len);
                 (e = tab[i]) != null;
                 i = nextIndex(i, len)) {
                ThreadLocal<?> k = e.get();
                if (k == null) {
                    e.value = null;
                    tab[i] = null;
                    size--;
                } else {
                    int h = k.threadLocalHashCode & (len - 1);
                    if (h != i) {
                        tab[i] = null;

                        // Unlike Knuth 6.4 Algorithm R, we must scan until
                        // null because multiple entries could have been stale.
                        while (tab[h] != null)
                            h = nextIndex(h, len);
                        tab[h] = e;
                    }
                }
            }
            return i;
        }

小結

一個 ThreadLocal 竟包含了如此多的知識,當你熟讀各種原始碼的時候,各種設計模式,各種細節,作者實現的非常有借鑑意義,理解了這樣的原始碼,在今後的專案中使用類似的,便可以知其然知其所以然,做到各種框架的深度定製,甚至實現自己的框架!

歡迎討論~

相關推薦

Java8 ThreadLocal 原始碼解析

前言 ThreadLocal ,像是一個神祕的黑衣人,令人望而生畏。唯有下定決心,一探究竟,方能解開他神祕的面紗、在Android中,Handler,EventBus,ConnectionPool 等等,都曾出現它的身影 是什麼東西? 看到Thread

ThreadLocal原始碼解析-Java8

目錄 一.ThreadLocal介紹   1.1 ThreadLocal的功能   1.2 ThreadLocal使用示例 二.原始碼分析-ThreadLocal   2.1 ThreadLocal的類層級關係   2.2 ThreadLocal的屬性欄位   2.3 建立ThreadLocal物件

Java ThreadLocal原始碼解析: ThreadLocalMap

ThreadLocalMap在比其中Thread和ThreadLocal部分要複雜很多,是ThreadLocal底層儲存和核心資料結構。從整體上將,ThreadLocalMap底層是Entry陣列,key值為ThreadLocal的hash code, 採用線性探測法解決雜湊衝突。 以下

Java ThreadLocal原始碼解析: Thread和ThreadLocal

之前對TreadLocal有所理解,對原理也有所瞭解,但一直不深入,重新整理,希望藉以加深理解和印象。 在Jdk1.8中,ThreadLocal相關程式碼主要分為三部分: Thread,其中Thread中儲存對ThreadLocal.ThreadLocalMap的引用,作為T

併發程式設計---ThreadLocal原始碼解析

    在遇到執行緒安全問題的時候,我們一般都是使用同步來解決,比如內建鎖、顯示鎖等等。執行緒安全的主要起因是因為多個執行緒同時操作一個共享變數,如果我們換種思路,在某些場景下,我們為這些執行緒提供共享變數的副本,讓他們在自己的私有域中去操作這些變數,執行緒之間互不影響,那是不是

Java8 HashMap原始碼解析

前言 Java7中的HashMap和Java8中的HashMap不太一樣,Java7中的HashMap主要是由陣列+連結串列組成的,而Java8中的 HashMap是由陣列+連結串列+紅黑樹組成的,當連結串列的長度超過8個時,就會轉為紅黑樹,降低查詢時的時間複

Java容器——HashMap(Java8原始碼解析(二)

在前文中介紹了HashMap中的重要元素,現在萬事俱備,需要刨根問底看看實現了。HashMap的增刪改查,都離不開元素查詢。查詢分兩部分,一是確定元素在table中的下標,二是確定以指定下標元素為首的具體位置。可以抽象理解為二維陣列,第一個通過雜湊函式得來,第二個下標則是連結串列或紅黑樹來得到,下面

Java容器——HashMap(Java8原始碼解析(一)

一 概述 HashMap是最常用的Java資料結構之一,是一個具有常數級別的存取速率的高效容器。相對於List,Set等,結構相對複雜,本篇我們先對HashMap的做一個基本說明,對組成元素和構造方法進行介紹。 二 繼承關係 首先看HashMap的繼承關係,比較簡單,實現了Map和序列化

Java 8 ThreadLocal 原始碼解析

Java 中的 ThreadLocal是執行緒內的區域性變數, 它為每個執行緒儲存變數的一個副本。ThreadLocal 物件可以在多個執行緒中共享, 但每個執行緒只能讀寫其中自己的副本。 目錄: 程式碼示例 原始碼解析 InheritableThreadLocal ThreadLoca

ThreadLocal 原始碼解析和使用

ThreadLocal定義 ThreadLocal是Java語言提供用於支援執行緒區域性變數的類。 ThreadLocal不是為了解決多執行緒訪問共享變數,而是通過為每個執行緒提供一個獨立的變數副本來解決變數併發訪問的衝突問題。 ThreadLocal

ThreadLocal原始碼解析,以及ThreadLocal、ThreadLocalMap、Thread 三者之間的關係

ThreadLocal、ThreadLocalMap、Thread 三者之間的關係 ThreadLocalMap 是 ThreadLocal 的內部類,Thread 中有個 ThreadLocalM

ThreadLocal原始碼解析

 ThreadLocal不是一個具體的執行緒。它是一個執行緒內部的資料儲存類,通過它可以再指定的執行緒中儲存資料,資料儲存以後,只有在指定執行緒中可以獲取到儲存的資料,對於其它執行緒來說則無法獲取到資料

ThreadLocal 原始碼解析

引言 ThreadLocal 可以在每個執行緒中存取資料,並且不同的執行緒中的資料互不影響。使用在資料以執行緒為作用域並且不同的執行緒擁有不用的資料副本,或者是複雜的引數傳遞時(引數在同一執行緒中的不同類中傳遞)。 在分析訊息機制原始碼的時候,涉及到了 Th

java8 ConcurrentHashMap原始碼解析

浪費了“黃金五年”的Java程式設計師,還有救嗎? >>>   

ThreadLocal原始碼解析,記憶體洩露以及傳遞性

我想ThreadLocal這東西,大家或多或少都瞭解過一點,我在接觸ThreadLocal的時候,覺得這東西很神奇,在網上看了很多部落格,也看了一些書,總覺得有一個坎跨不過去,所以對ThreadLocal一直是一知半解的,好在這東西在實際開發中畢竟用的不多,所以也就得過且過了。當然我說的“用的不多”,只是對於

Java原始碼解析ThreadLocal及使用場景

ThreadLocal是在多執行緒環境下經常使用的一個類。 這個類並不是為了解決多執行緒間共享變數的問題。舉個例子,在一個電商系統中,用一個Long型變量表示某個商品的庫存量,多個執行緒需要訪問庫存量進行銷售,並減去銷售數量,以更新庫存量。在這個場景中,是不能使用ThreadLocal類的。

Java8 ThreadLocal原始碼 詳解

JDK裡有一個ThreadLocal這麼一個類,其實起這個名字不是很貼近,這個類相當於給執行緒設定上了一個區域性變數。使得,不會因為多執行緒訪問同一個資源而產生多執行緒同步問題。 因為這個ThreadLocal類裡面放的是每個執行緒都擁有一個副本,執行緒之間彼此不會互相影響

ThreadLocal原始碼簡單解析

ThreadLocal    ThreadLocal我一開始接觸的時候,以為是“本地執行緒”搞的我雲裡霧裡的,看了內部實現後,這個Local應該稱為“區域性”。    在《多執行緒併發程式設計實戰》提到:維持執行緒封閉性的一種規範方法,這個類為每個使用該變數的執行緒都存有一份

Java ThreadLocal 的使用與原始碼解析

GitHub Page: http://blog.cloudli.top/posts/Java-ThreadLocal-的使用與原始碼解析/ ThreadLocal 主要解決的是每個執行緒繫結自己的值,可以將 ThreadLocal 看成全域性存放資料的盒子,盒子中儲存每個執行緒的私有資料。 驗證執行緒變數的

ThreadLocal<T> 原始碼解析

在activeJDBC框架內部的實現中看到了 ThreadLocal 這個類,記錄下了每個執行緒獨有的連線 private static final ThreadLocal<HashMap<String, Connection>> connectionsTL = new ThreadL