1. 程式人生 > >賭十包辣條,你一定沒見過這麼透徹的ThreadLocal講解

賭十包辣條,你一定沒見過這麼透徹的ThreadLocal講解

       如果轉載請宣告,轉自【https://www.cnblogs.com/andy-songwei/p/12040372.html】,謝謝!

       本文的主要內容為:

 

1、一個生活中的場景

       鑑於普羅大眾都喜歡看熱鬧,咱們先來看個熱鬧再開工吧!

 

場景一: 

 中午了, 張三、李四和王五一起去食堂大菜吃飯。食堂剛經營不久,還很簡陋,負責打菜的只有一位老阿姨。

 張三:我要一份雞腿。

 李四:我要一份小雞燉蘑菇。

 張三:我再要一份紅燒肉。

 王五:我要一份紅燒排骨。

 李四:我不要小雞燉蘑菇了,換成紅燒鯽魚。

 王五:我再要一份椒鹽蝦。

 張三:我再要一份梅菜扣肉。

 ......

 張三:我點的紅燒肉,為啥給我打紅燒鯽魚?

 李四:我的紅燒鯽魚呢?

 王五:我有點紅燒肉嗎?

 ......

 李四:我點了15元的菜,為啥扣我20?

 王五:我點了20元的菜,只扣了我15元,賺了,竊喜!

 張三:我已經刷了卡了,怎麼還叫我刷卡?

 ......

 老阿姨畢竟上了年紀,不那麼利索,這幾個小夥子咋咋呼呼,快言快語,老阿姨也被攪暈了,手忙腳亂,忙中出錯,這仨小夥也是怨聲載道。

 
場景二: 食堂領導看到這個場景,趕緊要求大家排隊,一個一個來。後來,老阿姨輕鬆多了,也沒有再犯錯了。 但是,新的問題又來了,打菜的人當中,很多妹子很磨嘰,點個菜猶猶豫豫想半天。 張三:太慢了,我快餓死了! 李四:再這麼慢,下次去別家! 王五:我等得花兒都謝啦! 趙六:啥?我點了啥菜,花了多少錢,其它人怎麼都知道?是阿姨多嘴了,還是其它人偷偷關注我很久了?太不安全了,一點隱私都沒有,以後不來了。 ...... 場景三: 領導聽到這些怨言,心裡很不是滋味,大手一揮:擴大經營,以後為你們每一個人開一個流動視窗並請一位私人阿姨,只為你一個人服務! 從此,再也沒有怨言,阿姨也沒有再犯錯了,皆大歡喜......

       場景一就像多個執行緒同時去操作一個數據,最終的結果就是混亂。於是出現了同步鎖synchronized,同一時刻只執行一個執行緒操作,就像場景二,大家先來後到排隊,混亂的問題解決了。但是此時一個執行緒在操作的時候,其它執行緒只能閒等著,而且這些資料是共享的,每個執行緒希望擁有隻能自己操作的私人資料,ThreadLocal就正好滿足了這個需求。

       所以,相比於synchronized,Threadlocal通過犧牲空間來換取時間和效率。

 

2、ThreadLocal簡介 

       ThreadLocal官方的介紹為:

1 /**
2  * This class provides thread-local variables.  These variables differ from
3  * their normal counterparts in that each thread that accesses one (via its
4  * {@code get} or {@code set} method) has its own, independently initialized
5  * copy of the variable.  {@code ThreadLocal} instances are typically private
6  * static fields in classes that wish to associate state with a thread (e.g.,
7  * a user ID or Transaction ID).
8  */

       大致意思是:ThreadLocal提供了執行緒本地變數。這些變數與一般變數相比,其不同之處在於,通過它的get()和set()方法,每個執行緒可以訪問自己獨立擁有的初始變數副本。翻譯成人話就是,ThreadLocal為每一個執行緒開闢了一個獨立的儲存器,只有對應的執行緒才能夠訪問其資料,其它執行緒則無法訪問。對應於前文的場景,就像食堂為每一個人安排了一個視窗和專屬阿姨為其打菜,這個過程中,這個視窗和阿姨就是其專屬的獨立的資源,其他人就無從知道他點了什麼菜,花了多少錢。

 

3、ThreadLocal的簡單使用示例 

    是騾子是馬,先拉出來溜溜!先直觀看看它的能耐,再來了解它豐富的內心:

 1 // =========例項3.1========
 2 private ThreadLocal<String> mThreadLocal = new ThreadLocal<>();
 3 private void testThreadLocal() throws InterruptedException {
 4     mThreadLocal.set("main-thread");
 5     Log.i("threadlocaldemo", "result-1=" + mThreadLocal.get());
 6     Thread thread_1 = new Thread() {
 7         @Override
 8         public void run() {
 9             super.run();
10             mThreadLocal.set("thread_1");
11             Log.i("threadlocaldemo", "result-2=" + mThreadLocal.get());
12         }
13     };
14     thread_1.start();
15     //該句表示thread_1執行完後才會繼續執行
16     thread_1.join();
17     Thread thread_2 = new Thread() {
18         @Override
19         public void run() {
20             super.run();
21             Log.i("threadlocaldemo", "result-3=" + mThreadLocal.get());
22         }
23     };
24     thread_2.start();
25     //該句表示thread_2執行完後才會繼續執行
26     thread_2.join();
27     Log.i("threadlocaldemo", "result-4=" + mThreadLocal.get());
28 }

 在主執行緒中呼叫這個方法,執行結果:

1 12-13 13:42:50.117 25626-25626/com.example.demos I/threadlocaldemo: result-1=main-thread
2 12-13 13:42:50.119 25626-25689/com.example.demos I/threadlocaldemo: result-2=thread_1
3 12-13 13:42:50.119 25626-25690/com.example.demos I/threadlocaldemo: result-3=null
4 12-13 13:42:50.120 25626-25626/com.example.demos I/threadlocaldemo: result-4=main-thread

       看到這個結果會不會驚掉下巴呢?明明在第9行中set了值,第10行中也得到了對應的值,但第20行的get得到的卻是null,第26行得到的是第3行set的值。這就是ThreadLocal的神奇功效,主執行緒set的值,只能在主執行緒get到;thread_1內部set的值,thread_1中才能get;thread_2中沒有set,所以get到的就是null。

       而實現這,不要999,也不要99,只要3......三步即可:

1 ThreadLocal<T> mThreadLocal = new ThreadLocal<>();
2 mThreadLocal.set(T);
3 mThreadLocal.get();

就是這麼方便,就是這麼簡潔!

 

4、提供的4個主要介面

       ThreadLocal以其使用簡單,風格簡潔讓人一見傾心。它對外提供的介面很少,當前SDK中,主要有4個:

1 public void set(T value) { }  
2 public T get() { }  
3 public void remove() { }  
4 protected T initialValue() { }  

為了保持對這些方法說明的原滋原味,我們直接通過原始碼中對其的註釋說明來認識它們。

 (1)set()

 1 /**
 2  * Sets the current thread's copy of this thread-local variable
 3  * to the specified value.  Most subclasses will have no need to
 4  * override this method, relying solely on the {@link #initialValue}
 5  * method to set the values of thread-locals.
 6  *
 7  * @param value the value to be stored in the current thread's copy of
 8  *        this thread-local.
 9  */
10 public void set(T value)

設定當前執行緒的ThreadLocal值為指定的value。大部分子類沒有必要重寫該方法,可以依賴initialValue()方法來設定ThreadLocal的值。

  (2)get()

1 /**
2  * Returns the value in the current thread's copy of this
3  * thread-local variable.  If the variable has no value for the
4  * current thread, it is first initialized to the value returned
5  * by an invocation of the {@link #initialValue} method.
6  *
7  * @return the current thread's value of this thread-local
8  */
9 public T get()

用於獲取當前執行緒所對應的ThreadLocal值。如果當前執行緒下,該變數沒有值,會通過呼叫initialValue()方法返回的值對其進行初始化。

  (3)remove()

 1 /**
 2  * Removes the current thread's value for this thread-local
 3  * variable.  If this thread-local variable is subsequently
 4  * {@linkplain #get read} by the current thread, its value will be
 5  * reinitialized by invoking its {@link #initialValue} method,
 6  * unless its value is {@linkplain #set set} by the current thread
 7  * in the interim.  This may result in multiple invocations of the
 8  * {@code initialValue} method in the current thread.
 9  *
10  * @since 1.5
11  */
12  public void remove()

       該介面是從JDK1.5開始提供的,用於刪除當前執行緒對應的ThreadLocal值,從而減少記憶體佔用。在同一執行緒中,如果該方法被呼叫了,隨後再呼叫get()方法時,會使得initialValue()被呼叫,從而ThreadLocal的值被重新初始化,除非此時在呼叫get()前呼叫了set()來賦值。該方法可能導致initialValue()被多次呼叫。該方法可以不用顯示呼叫,因為當執行緒結束後,系統會自動回收執行緒區域性變數值。所以該方法不是必須呼叫的,只不過顯示呼叫可以加快記憶體回收。

  (4)initialValue()

 1 /**
 2  * Returns the current thread's "initial value" for this
 3  * thread-local variable.  This method will be invoked the first
 4  * time a thread accesses the variable with the {@link #get}
 5  * method, unless the thread previously invoked the {@link #set}
 6  * method, in which case the {@code initialValue} method will not
 7  * be invoked for the thread.  Normally, this method is invoked at
 8  * most once per thread, but it may be invoked again in case of
 9  * subsequent invocations of {@link #remove} followed by {@link #get}.
10  *
11  * <p>This implementation simply returns {@code null}; if the
12  * programmer desires thread-local variables to have an initial
13  * value other than {@code null}, {@code ThreadLocal} must be
14  * subclassed, and this method overridden.  Typically, an
15  * anonymous inner class will be used.
16  *
17  * @return the initial value for this thread-local
18  */
19 protected T initialValue() {
20     return null;
21 }

       返回當前執行緒對應的ThreadLocal的初始值。噹噹前執行緒是通過get()方法第一次對ThreadLocal進行訪問時,該方法將會被呼叫,除非當前執行緒之前呼叫過set()方法,在這種情況下initialValue()方法將不會被當前執行緒所呼叫。一般而言,該方法最多隻會被每個執行緒呼叫一次,除非隨後在當前執行緒中呼叫remove()方法,然後呼叫get()方法。該實現會簡單地返回null;如果程式設計師希望ThreadLocal擁有一個初始值,而不是null,ThreadLocal需要定義一個子類,並且在子類中重寫initialValue()方法。比較典型的做法是使用一個匿名內部類。該方法由protected修飾,可見其這樣設計通常是為了供使用者重寫,從而自定義初始值。後面會再通過例項來演示該方法的使用。

 

5、ThreadLocal工作機制

       ThreadLocal使用起來非常簡單,但它是如何實現為每一個Thread儲存一份獨立的資料的呢?我們先結合例項3.1來看set()方法都做了些什麼:

1 //=========ThreadLocal=======原始碼5.1
2 public void set(T value) {
3     Thread t = Thread.currentThread();
4     ThreadLocalMap map = getMap(t);
5     if (map != null)
6         map.set(this, value);
7     else
8         createMap(t, value);
9 }

       首先就是獲取當前的執行緒,然後根據當前執行緒來獲取一個ThreadLocalMap,如果map不為null,就往map中插入指定值,注意這的key是ThreadLocal例項;如果map為null,就建立一個map。看看第4行getMap(t)做了啥:

 1 //=========ThreadLocal=======原始碼5.2
 2 /**
 3  * Get the map associated with a ThreadLocal. 
 4  * ......
 5  */
 6 ThreadLocalMap getMap(Thread t) {
 7     return t.threadLocals;
 8 }
 9 
10 /**
11  * ThreadLocalMap is a customized hash map suitable only for
12  * maintaining thread local values......
13  */
14 static class ThreadLocalMap {
15      ......
16 }
17 
18 //==========Thread========
19 ThreadLocal.ThreadLocalMap threadLocals = null;

       getMap()返回的是指定執行緒(也就是當前執行緒)的threadLocals變數,這個變數是ThreadLocal.ThreadLocalMap型別的,而ThreadLocalMap是一個僅適用於維護執行緒本地變數值的自定義的HashMap。簡單來說,就是返回當前執行緒下的一個自定義HashMap。

       下面我抽取了ThreadLocalMap的部分程式碼,先來總體上認識它(這裡我們不需要讀懂其中的每一行程式碼,知道它裡面主要做了哪些事就可以了):

  1 //=========原始碼5.3========
  2 static class ThreadLocalMap {
  3 
  4     static class Entry extends WeakReference<ThreadLocal<?>> {
  5         /** The value associated with this ThreadLocal. */
  6         Object value;
  7 
  8         Entry(ThreadLocal<?> k, Object v) {
  9             super(k);
 10             value = v;
 11         }
 12     }
 13 
 14     /**
 15      * The initial capacity -- MUST be a power of two.
 16      */
 17     private static final int INITIAL_CAPACITY = 16;
 18 
 19     /**
 20      * The table, resized as necessary.
 21      * table.length MUST always be a power of two.
 22      */
 23     private Entry[] table;
 24 
 25     /**
 26      * The number of entries in the table.
 27      */
 28     private int size = 0;
 29 
 30     /**
 31      * The next size value at which to resize.
 32      */
 33     private int threshold; // Default to 0
 34 
 35     /**
 36      * Set the resize threshold to maintain at worst a 2/3 load factor.
 37      */
 38     private void setThreshold(int len) {
 39         threshold = len * 2 / 3;
 40     }
 41     
 42     ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
 43         table = new Entry[INITIAL_CAPACITY];
 44         int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
 45         table[i] = new Entry(firstKey, firstValue);
 46         size = 1;
 47         setThreshold(INITIAL_CAPACITY);
 48     }
 49     
 50     /**
 51      * Get the entry associated with key.
 52      * ......
 53      */
 54     private Entry getEntry(ThreadLocal<?> key) {
 55         int i = key.threadLocalHashCode & (table.length - 1);
 56         Entry e = table[i];
 57         if (e != null && e.get() == key)
 58             return e;
 59         else
 60             return getEntryAfterMiss(key, i, e);
 61     }
 62 
 63     /**
 64      * Set the value associated with key.
 65      * ......
 66      */
 67     private void set(ThreadLocal<?> key, Object value) {
 68 
 69         // We don't use a fast path as with get() because it is at
 70         // least as common to use set() to create new entries as
 71         // it is to replace existing ones, in which case, a fast
 72         // path would fail more often than not.
 73 
 74         Entry[] tab = table;
 75         int len = tab.length;
 76         int i = key.threadLocalHashCode & (len-1);
 77 
 78         for (Entry e = tab[i];
 79              e != null;
 80              e = tab[i = nextIndex(i, len)]) {
 81             ThreadLocal<?> k = e.get();
 82 
 83             if (k == key) {
 84                 e.value = value;
 85                 return;
 86             }
 87 
 88             if (k == null) {
 89                 replaceStaleEntry(key, value, i);
 90                 return;
 91             }
 92         }
 93 
 94         tab[i] = new Entry(key, value);
 95         int sz = ++size;
 96         if (!cleanSomeSlots(i, sz) && sz >= threshold)
 97             rehash();
 98     }
 99 
100     /**
101      * Remove the entry for key.
102      */
103     private void remove(ThreadLocal<?> key) {
104         Entry[] tab = table;
105         int len = tab.length;
106         int i = key.threadLocalHashCode & (len-1);
107         for (Entry e = tab[i];
108              e != null;
109              e = tab[i = nextIndex(i, len)]) {
110             if (e.get() == key) {
111                 e.clear();
112                 expungeStaleEntry(i);
113                 return;
114             }
115         }
116     }
117 
118     /**
119      * Double the capacity of the table.
120      */
121     private void resize() {
122        ......
123     }
124 }
View Code

       這裡面維護了一個Entry[] table陣列,初始容量為16,當資料超過當前容量的2/3時,就開始擴容,容量增大一倍。每一個Entry的K為ThreadLocal物件,V為要儲存的值。每一個Entry在陣列中的位置,是根據其K(即ThreadLocal物件)的hashCode & (len - 1)來確定,如第44行所示,這裡K的hashCode是系統給出的一個演算法計算得到的。如果碰到K的hashCode值相同,即hash碰撞的場景,會採用尾插法形成連結串列。當對這個map進行set,get,remove操作的時候,也是通過K的hashCode來確定該Entry在table中的位置的,採用hashCode來查詢資料,效率比較高。這也是HashMap底層實現的基本原理,如果研究過HashMap原始碼,這段程式碼就應該比較容易理解了。

       繼續看原始碼5.1,第一次呼叫的時候,顯然map應該是null,就要執行第8行createMap了,

1 //==========ThreadLocal=========原始碼5.4
2 void createMap(Thread t, T firstValue) {
3     t.threadLocals = new ThreadLocalMap(this, firstValue);
4 }

       結合ThreadLocalMap原始碼第41行的構造方法,就清楚了這個方法建立了一個ThreadLocalMap物件,並存儲了一個Entry<當前的ThreadLocal物件,value>。此時,在當前的執行緒下擁有了一個ThreadLocalMap,這個ThreadLocalMap中維護了一個容量為16的table,table中儲存了一個以當前的ThreadLocal物件為K,value值為V的Entry。Thread、ThreadLocalMap、ThreadLocal、Entry之間的關係可以表示為下圖:

 

 圖5.1

       而如果當前Thread的map已經存在了,原始碼5.1就會執行第6行了,進而執行ThreadLocalMap中的set方法。結合前面對ThreadLocalMap的介紹,想必這個set方法也容易理解了,大致過程是:

    1)根據Thread找到map;

    2)通過傳入的this(即ThreadLocal物件),得到hashCode;

    3)根據hashCode & (len - 1)確定對應Entry在table中的位置;

    4)如果該Entry存在,則替換Value,否則新建(ThreadLocalMap原始碼第78~92行表示在具有相同hashCode的Entry連結串列上找到對應的Entry,這和hash碰撞有關)。

 

       在呼叫ThreadLocal的get方法時又做了什麼呢?看看其原始碼:

 1 //=========ThreadLocal======原始碼5.5
 2 public T get() {
 3     Thread t = Thread.currentThread();
 4     ThreadLocalMap map = getMap(t);
 5     if (map != null) {
 6         ThreadLocalMap.Entry e = map.getEntry(this);
 7         if (e != null) {
 8             @SuppressWarnings("unchecked")
 9             T result = (T)e.value;
10             return result;
11         }
12     }
13     return setInitialValue();
14 }

       現在,第12行及以前的程式碼應該很容易理解了,結合ThreadLocalMap中的get原始碼,我們再梳理一下:

    1)根據Thread找到自己的map;

    2)在map中通過this(即ThreadLocal物件)得到hashCode;

    3)通過hashCode & (len-1)找到對應Entry在table中的位置;

    4)返回Entry的value。

       而如果map為null,或者在map中找到的Entry為null,那麼就執行第20行了。

 1 //==========ThreadLocal========原始碼5.6
 2 private T setInitialValue() {
 3     T value = initialValue();
 4     Thread t = Thread.currentThread();
 5     ThreadLocalMap map = getMap(t);
 6     if (map != null)
 7         map.set(this, value);
 8     else
 9         createMap(t, value);
10     return value;
11 }
12 
13 protected T initialValue() {
14     return null;
15 }
第13行的initialValue()方法,前面介紹過,可以讓子類重寫,即給ThreadLocal指定初始值;如果沒有重寫,那返回值就是null。第4~9行前面也介紹過了,使用或者建立map來存入該值。

最後還一個remove()方法

1 //======ThreadLocal======
2 public void remove() {
3     ThreadLocalMap m = getMap(Thread.currentThread());
4     if (m != null)
5         m.remove(this);
6 }

結合ThrealLocalMap中的remove方法,完成對ThreadLocal值的刪除。其大致流程為:

    1)根據當前Thread找到其map;

    2)根據ThreadLocal物件得到hashCode;

    3)通過hashCode & (len -1)找到在table中的位置;

    4)在table中查詢對應的Entry,如果存在則刪除。

 

       總結:通過對提供的4個介面方法的分析,我們應該就能清楚了,ThreadLocal之所以能夠為每一個執行緒維護一個副本,是因為每個執行緒都擁有一個map,這個map就是每個執行緒的專屬空間。也就是存在下面的關係圖(不用懷疑,該圖和圖5.1相比,只是少了容量大小):

結合這一節對ThreadLocal機制的介紹,例項3.1執行後的就存在如下的資料結構了:

 

 

6、ThreadLocal在Looper中的使用

       ThreadLocal在系統原始碼中有很多地方使用,最典型的地方就是Handler的Looper中了。這裡結合Looper中的原始碼,來了解一下ThreadLocal在系統原始碼中的使用。

       我們知道,在一個App程序啟動的時候,會在ActiivtyThread類的main方法,也就是App的入口方法中,會為主執行緒準備一個Looper,如下程式碼所示:

1 //======ActivityTread======原始碼6.1
2 public static void main(String[] args) {
3       ......
4       Looper.prepareMainLooper();
5       ......
6 }

而在子執行緒中例項Handler的時候,總是需要顯示呼叫Looper.prepare()方法來為當前執行緒生成一個Looper物件,以及通過Looper.myLooper()來得到自己執行緒的Looper來傳遞給Handler。

Looper中相關的關鍵原始碼如下:

 1 //==========Looper========原始碼6.2
 2 
 3 // sThreadLocal.get() will return null unless you've called prepare().
 4 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
 5 private static Looper sMainLooper;
 6 
 7 /**
 8  * Initialize the current thread as a looper, marking it as an
 9  * application's main looper. The main looper for your application
10  * is created by the Android environment, so you should never need
11  * to call this function yourself.  See also: {@link #prepare()}
12  */
13 public static void prepareMainLooper() {
14     prepare(false);
15     synchronized (Looper.class) {
16         if (sMainLooper != null) {
17             throw new IllegalStateException("The main Looper has already been prepared.");
18         }
19         sMainLooper = myLooper();
20     }
21 }
22 
23 /**
24  * Return the Looper object associated with the current thread.  Returns
25  * null if the calling thread is not associated with a Looper.
26  */
27 public static @Nullable Looper myLooper() {
28     return sThreadLocal.get();
29 }
30 
31 /** Initialize the current thread as a looper.
32   * ......
33   */
34 public static void prepare() {
35     prepare(true);
36 }
37 private static void prepare(boolean quitAllowed) {
38     if (sThreadLocal.get() != null) {
39         throw new RuntimeException("Only one Looper may be created per thread");
40     }
41     sThreadLocal.set(new Looper(quitAllowed));
42 }
43 
44 /**
45  * Returns the application's main looper, which lives in the main thread of the application.
46  */
47 public static Looper getMainLooper() {
48     synchronized (Looper.class) {
49         return sMainLooper;
50     }
51 }

我們可以看到不少ThreadLocal的影子,Looper也正是通過ThreadLocal來為每個執行緒維護一份Looper例項的。通過我們前文的介紹,這裡應該能夠輕而易舉理解其中的運作機制了吧,這裡就再不囉嗦了。

 

7、實踐是檢驗真理的唯一標準

        前面介紹了ThreadLocal提供的四個介面,以及詳細講解了它的工作原理。現在我們將例項3.1做一些修改,將各個介面的功能都包含進來,並稍微增加一點複雜度,如果能夠看懂這個例項,就算是真的理解ThreadLocal了。

 1 //=========例項7.1=======
 2 private ThreadLocal<String> mStrThreadLocal = new ThreadLocal<String>() {
 3     @Override
 4     protected String initialValue() {
 5         Log.i("threadlocaldemo", "initialValue");
 6         return "initName";
 7     }
 8 };
 9 private ThreadLocal<Long> mLongThreadLocal = new ThreadLocal<>();
10 private void testThreadLocal() throws InterruptedException {
11     mStrThreadLocal.set("main-thread");
12     mLongThreadLocal.set(Thread.currentThread().getId());
13     Log.i("threadlocaldemo", "result-1:name=" + mStrThreadLocal.get() + ";id=" + mLongThreadLocal.get());
14     Thread thread_1 = new Thread() {
15         @Override
16         public void run() {
17             super.run();
18             mStrThreadLocal.set("thread_1");
19             mLongThreadLocal.set(Thread.currentThread().getId());
20             Log.i("threadlocaldemo", "result-2:name=" + mStrThreadLocal.get() + ";id=" + mLongThreadLocal.get());
21         }
22     };
23     thread_1.start();
24     //該句表示thread_1執行完後才會繼續執行
25     thread_1.join();
26     Thread thread_2 = new Thread() {
27         @Override
28         public void run() {
29             super.run();
30             Log.i("threadlocaldemo", "result-3:name=" + mStrThreadLocal.get() + ";id=" + mLongThreadLocal.get());
31         }
32     };
33     thread_2.start();
34     //該句表示thread_2執行完後才會繼續執行
35     thread_2.join();
36     mStrThreadLocal.remove();
37     Log.i("threadlocaldemo", "result-4:name=" + mStrThreadLocal.get() + ";id=" + mLongThreadLocal.get());
38 }

在主執行緒中執行該方法,執行結果為:

1 12-14 16:25:40.662 4844-4844/com.example.demos I/threadlocaldemo: result-1:name=main-thread;id=2
2 12-14 16:25:40.668 4844-5351/com.example.demos I/threadlocaldemo: result-2:name=thread_1;id=926
3 12-14 16:25:40.669 4844-5353/com.example.demos I/threadlocaldemo: initialValue
4 12-14 16:25:40.669 4844-5353/com.example.demos I/threadlocaldemo: result-3:name=initName;id=null
5 12-14 16:25:40.669 4844-4844/com.example.demos I/threadlocaldemo: initialValue
6 12-14 16:25:40.669 4844-4844/com.example.demos I/threadlocaldemo: result-4:name=initName;id=2

此時存在的資料結構為:

       對於這份log和資料結構圖,這裡就不再一一講解了,如果前面都看懂了,這些都是小菜一碟。

 

結語

       對ThreadLocal的講解這裡就結束了,能讀到這裡,也足以說明你是人才,一定前途無量,祝你好運,早日走上人生巔峰!

       由於經驗和水平有限,有描述不當或不準確的地方,還請不吝賜教,謝謝!