ThreadLocal原理
1. ThreadLocal是什麼
ThreadLocal提供執行緒區域性變數。這些變數與普通的變數不同之處在於,每個訪問這種變數的執行緒(通過它的get或set方法)都有自己的、獨立初始化的變數副本。
ThreadLocal例項通常是希望將狀態關聯到一個執行緒的類的私有靜態欄位(比如,user ID 或者 Transaction ID 等等)。
(
畫外音:這段話表達了三個意思
- ThreadLocal是一種變數型別,我們稱之為“執行緒區域性變數”
- 每個執行緒訪問這種變數的時候都會建立該變數的副本,這個變數副本為執行緒私有
- ThreadLocal型別的變數一般用private static加以修飾
)
例如,下面的例子中這個類為每個執行緒生成唯一標識。一個執行緒的id是它第一次呼叫ThreadId.get()方法指定的。
package com.cjs.example; import java.util.concurrent.atomic.AtomicInteger; public class ThreadId { private static final AtomicInteger nextId = new AtomicInteger(0); private static final ThreadLocal<Integer> threadId = new ThreadLocal<Integer>() { @Override protected Integer initialValue() { return nextId.getAndIncrement(); } }; public static int get() { return threadId.get(); } public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getId()); } }).start(); } } }
2. ThreadLocal主要操作
/** * 返回當前執行緒對ThreadLocal變數的“初始值” * 這個方法將線上程第一次訪問變數(通過呼叫get方法)時被呼叫,如果之前已經呼叫過了就不會再調了 * * @return the initial value for this thread-local */ protected T initialValue() { return null; } /** * 設定當前執行緒的ThreadLocal變數的副本為指定的值 * * @param value the value to be stored in the current thread's copy of this thread-local. */ public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); } /** * 返回當前執行緒的ThreadLocal變數副本的值 * * @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(); } /** * 刪除當前執行緒的ThreadLocal變數副本的值 */ public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); }
3. 閱讀原始碼
3.1. set方法
可以看到,ThreadLocalMap底層是一個數組,陣列中元素型別是Entry型別
set操作是向當前執行緒的ThreadLocal.ThreadLocalMap型別的成員變數threadLocals中設定值,key是this,value是我們指定的值
注意,這裡傳的this代表的是那個ThreadLocal型別的變數(或者說叫物件)
也就是說,每個執行緒都維護了一個ThreadLocal.ThreadLocalMap型別的物件,而set操作其實就是以ThreadLocal變數為key,以我們指定的值為value,最後將這個鍵值對封裝成Entry物件放到該執行緒的ThreadLocal.ThreadLocalMap物件中。每個ThreadLocal變數在該執行緒中都是ThreadLocal.ThreadLocalMap物件中的一個Entry。既然每個ThreadLocal變數都對應ThreadLocal.ThreadLocalMap中的一個元素,那麼就可以對這些元素進行讀寫刪除操作。
3.2. get方法
get()方法就是從當前執行緒的ThreadLocal.ThreadLocalMap物件中取出對應的ThreadLocal變數所對應的值
同理,remove()方法就是清除這個值
用圖形表示的話,大概是這樣的:
或者是這樣的:
4. ThreadLocal使用場景
- 線上程生命週期內傳值
最後,一切都歸功於ThreadLocalMap