Windows Server 2012 R2 遠端桌面服務部署指南
一句話描述
threadLocal,是執行緒本地變數,每個thread例項持有一個ThreadLocalMap型別的threadLocals屬性,map的key為ThreadLocal變數,value為呼叫threadLocal.set()的值,也就是真正有用的資料。
基礎
簡單例子
package com.xy.thread.threadlocal.ch01; /** * <p> * 常用寫法: * 1.如果需要設定初始值,可以使用匿名類,或者定義子類繼承Threadlocal * 2.不需要初始值可以直接new ThreadLocal()建立物件 * </p> * * @author liufuquan * @since 2022-02-15 */ public class NumService implements Runnable{ //匿名類語法,繼承類或者實現介面 // initialValue方法為protected方法,繼承ThreadLocal類重寫initialValue方法設定初始值 private ThreadLocal<Integer> numThreadLocal = new ThreadLocal<Integer>() { @Override protected Integer initialValue() { return 0; } }; @Override public void run() { for (int i = 0; i < 5; i++) { Integer integer = numThreadLocal.get(); System.out.println(Thread.currentThread().getName() + ":" + integer); numThreadLocal.set(++integer); } //使用完之後,要清除 numThreadLocal.remove(); } }
package com.xy.thread.threadlocal.ch01; import java.util.Scanner; /** * <p> * * </p> * * @author liufuquan * @since 2022-02-15 */ public class NumServiceTest { public static void main(String[] args) { for (int i = 0; i < 2; i++) { new Thread(new NumService()).start(); } Scanner input = new Scanner(System.in); input.nextLine(); } }
執行緒關聯的原理
簡介
/**
* This class provides thread-local variables.
**/
public class ThreadLocal<T> {}
thread-local variables,官方的註釋,說明它的含義,執行緒本地變數。
ThreadLocal 並不是一個獨立的存在, 它與 Thread 類是存在耦合的, java.lang.Thread 類針對 ThreadLocal 提供瞭如下支援:
/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. 與此執行緒有關的 ThreadLocal 值。此對映由 ThreadLocal 類維護*/ ThreadLocal.ThreadLocalMap threadLocals = null;
每個執行緒thread物件中有一個Thread.ThreadLocalMap欄位,使用時,呼叫ThreadLocal物件的set時,放到threadLocalMap中,key為ThreadLocal,value為設定的值。(每個執行緒都將自己維護一個 ThreadLocal.ThreadLocalMap 類在上下文中; map的key是ThreadLocal物件,value是與執行緒繫結的,真正用的用到的值。 )
uml關係
程式碼解析
ThreadLocal.set()方法,set 方法其實是將 target value 放到當前執行緒的 ThreadLocalMap 中, 而 ThreadLocal 類自己僅僅作為該 target value 所對應的 key
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
get 方法也是類似的道理, 從執行緒的 ThreadLocalMap 中獲取以當前 ThreadLocal 為 key 對應的 value
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();
}
如果沒有 set 過 value, 此處 get() 將返回 null或者initialValue()設定的預設值。不過 initialValue() 方法是一個 protected 方法, 需要重寫邏輯實現自定義的初始預設值。
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
protected T initialValue() {
return null;
}
使用場景
Spring中@Transactional註解,使用ThreadLocal儲存資料庫連線connection,保證事務方法每次拿到的都是同一個connection
Spring中Bean在singleton作用域時,使用ThreadLocal解決共享變數的執行緒安全問題
進階
ThreadLocalMap資料結構
ThreadLocalMap是雜湊表,但是與HashMap不同的是,出現hash衝突後的解決辦法:hashmap使用拉鍊法,ThreadLocalMap使用線性探索法,發生衝突就在陣列中向後尋找空的位置插入。
InheritableThreadLocal
一句話描述
InheritableThreadLocal,可繼承的執行緒本地變數,每個執行緒例項持有ThreadLocalMap型別的inheritableThreadLocals,父執行緒建立子執行緒時會將父執行緒的inheritableThreadLocals複製到子執行緒的inheritableThreadLocals,從而實現執行緒本地變數可以傳遞到子執行緒。
InheritableThreadLocals類通過重寫getMap和createMap兩個方法將本地變數儲存到了具體執行緒的inheritableThreadLocals變數中,當執行緒通過InheritableThreadLocals例項的set或者get方法設定變數的時候,就會建立當前執行緒的inheritableThreadLocals變數。而父執行緒建立子執行緒的時候,ThreadLocalMap中的建構函式會將父執行緒的inheritableThreadLocals中的變數「複製一份到子執行緒的inheritableThreadLocals」變數中。
class Thread implements Runnable{
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
}
執行緒的init方法
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
//(1)獲取當前執行緒(父執行緒)
Thread parent = currentThread();
//(2)父執行緒的inheritableThreadLocal和inheritThreadLocals=true
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
//(3)父執行緒的inheritableThreadLocals賦值給子執行緒的inheritableThreadLocals
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
}
參考資料
https://mp.weixin.qq.com/s/x_dpL66o5S7x_PjncWLvUw
https://mp.weixin.qq.com/s/KbB_3JdR56Mw8O2gJEP2OA