Threadlocal詳解(ThreadLocal,InheritTableThreadLocal,TransmittableThreadLocal)
參考:https://blog.csdn.net/upgroup/article/details/107935832
主要從四個問題了解
1)ThreadLocal可以用來解決什麼問題,你工作中或者使用的框架中哪裡用到了?
2)ThreadLocal解決不了什麼問題?怎麼辦
3)InheritThreadLocal解決不了什麼問題,怎麼辦
4)阿里的TransmittableThreadLocal是如何解決這個問題的
ThreadLocal可以用來解決什麼問題
一個變數的值想要線上程中傳遞,比如說分散式呼叫跟蹤系統中的traceID,RPC框架中傳遞上下文的context
保證執行緒不安全的類的安全,比如說SimpleDateFormate
ThreadLocal解決不了問題怎麼辦
ThreadLocal只是執行緒傳遞,但是當在本執行緒中建立了一個新的執行緒,比如說又new了一個Thread的情況下,就不能把ThreadLocal中的資料傳遞給子執行緒。此時解決辦法是通過InheritThreadLocal來解決
InheritThreadLocal解決不了什麼問題,怎麼辦
inheritThreadlocal解決了建立新的子執行緒的傳遞問題,但是如果用執行緒池建立執行緒的話,使用InheritThreadLocal可以保證資料的傳遞。但是執行緒池中的執行緒是重複使用的,當重複使用執行緒的時候,重複使用的執行緒中的InheritThreadLocal仍然是上次建立的資料。解決辦法就是阿里的TransmittableThreadLocal
阿里的TransmittableThreadLocal是如何解決這個問題的
阿里的解決方式仍然是使用的InheritThreadLocal,不同的是,阿里通過Javaagent修改了執行緒池的位元組碼,線上程池建立Runnable或者Callable的時候進行了包裝,我們就叫RunnableWrapper。把需要傳遞的資料在new RunnableWrapper的時候就傳遞到了RunnableWrapper的成員變數中。在RunnableWrapper執行run方法的時候,先將成員變數的資料重新放一遍ThreadLocal,然後再真正執行被包裝的Runnable的run方法。這樣在真正的run方法中就可以拿到ThreadLocal的資料
inheritThreadlocal詳解
inheritThreadlocal類
重寫了ThreadLocal的三個函式
/**
* 該函式在父執行緒建立子執行緒,向子執行緒複製InheritableThreadLocal變數時使用
*/
protected T childValue(T parentValue) {
return parentValue;
}
/**
* 由於重寫了getMap,操作InheritableThreadLocal時,
* 將隻影響Thread類中的inheritableThreadLocals變數,
* 與threadLocals變數不再有關係
*/
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
/**
* 類似於getMap,操作InheritableThreadLocal時,
* 將隻影響Thread類中的inheritableThreadLocals變數,
* 與threadLocals變數不再有關係
*/
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
執行緒間傳值實現原理
首先講解一下Thread類
public class Thread implements Runnable {
......(其他原始碼)
/*
* 當前執行緒的ThreadLocalMap,主要儲存該執行緒自身的ThreadLocal
*/
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal,自父執行緒整合而來的ThreadLocalMap,
* 主要用於父子執行緒間ThreadLocal變數的傳遞
* 本文主要討論的就是這個ThreadLocalMap
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
......(其他原始碼)
}
Thread類包含Threadlocals和inherittableThreadLocals兩個變數inheritableThreadLocals 即主要儲存可自動向子執行緒中傳遞的ThreadLocal.ThreadLocalMap。
接下來看一下父執行緒建立子執行緒的流程,我們從最簡單的方式說起:
使用者建立Thread
Thread thread = new Thread();
Thread建立
/**
* Allocates a new {@code Thread} object. This constructor has the same
* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
* {@code (null, null, gname)}, where {@code gname} is a newly generated
* name. Automatically generated names are of the form
* {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
*/
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
Thread初始化
/**
* 預設情況下,設定inheritThreadLocals可傳遞
*/
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
/**
* 初始化一個執行緒.
* 此函式有兩處呼叫,
* 1、上面的 init(),不傳AccessControlContext,inheritThreadLocals=true
* 2、傳遞AccessControlContext,inheritThreadLocals=false
*/
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
......(其他程式碼)
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
......(其他程式碼)
}
可以看到,採用預設方式產生子執行緒時,inheritThreadLocals=true;若此時父執行緒inheritableThreadLocals不為空,則將父執行緒inheritableThreadLocals傳遞至子執行緒。
ThreadLocal.createInheritedMap
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
/**
* 構建一個包含所有parentMap中Inheritable ThreadLocals的ThreadLocalMap
* 該函式只被 createInheritedMap() 呼叫.
*/
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
// ThreadLocalMap 使用 Entry[] table 儲存ThreadLocal
table = new Entry[len];
// 逐一複製 parentMap 的記錄
for (int j = 0; j < len; j++) {
Entry e = parentTable[j];
if (e != null) {
@SuppressWarnings("unchecked")
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if (key != null) {
// 可能會有同學好奇此處為何使用childValue,而不是直接賦值,
// 畢竟childValue內部也是直接將e.value返回;
// 個人理解,主要為了減輕閱讀程式碼的難度
Object value = key.childValue(e.value);
Entry c = new Entry(key, value);
int h = key.threadLocalHashCode & (len - 1);
while (table[h] != null)
h = nextIndex(h, len);
table[h] = c;
size++;
}
}
}
}
子執行緒將parentMap逐一複製到自身執行緒
InheritableThreadLocal主要用於子執行緒建立時,需要自動繼承父執行緒的ThreadLocal變數,方便必要資訊的進一步傳遞。
TransmittableThreadLocal詳解
用於解決使用執行緒池快取執行緒的元件的情況下傳遞ThreadLocal
使用場景:分散式跟蹤系統,應用容器或上下層框架跨應用程式碼給下層SDK傳遞資訊,日誌收集系統上下文,
原始碼分析:
TransmittableThreadLocal 繼承自 InheritableThreadLocal,這樣可以在不破壞ThreadLocal 本身的情況下,使得當使用者利用 new Thread() 建立執行緒時仍然可以達到傳遞InheritableThreadLocal 的目的。
public class TransmittableThreadLocal<T> extends InheritableThreadLocal<T>{......}
TransmittableThreadLocal相比於InheritableThreadLocal比較關鍵的一點是引入了holder變數,這樣就不必對外暴露Thread中的inherittablethreadlocals變數儲存ThreadLocalMap的封裝性
// 理解holder,需注意如下幾點:
// 1、holder 是 InheritableThreadLocal 變數;
// 2、holder 是 static 變數;
// 3、value 是 WeakHashMap;
// 4、深刻理解 ThreadLocal 工作原理;
private static InheritableThreadLocal<Map<TransmittableThreadLocal<?>, ?>> holder =
new InheritableThreadLocal<Map<TransmittableThreadLocal<?>, ?>>() {
@Override
protected Map<TransmittableThreadLocal<?>, ?> initialValue() {
return new WeakHashMap<>();
}
@Override
protected Map<TransmittableThreadLocal<?>, ?> childValue(Map<TransmittableThreadLocal<?>, ?> parentValue) {
return new WeakHashMap<>(parentValue);
}
};
// 呼叫 get() 方法時,同時將 this 指標放入 holder
public final T get() {
T value = super.get();
if (null != value) {
addValue();
}
return value;
}
void addValue() {
if (!holder.get().containsKey(this)) {
holder.get().put(this, null); // WeakHashMap supports null value.
}
}
// 呼叫 set() 方法時,同時處理 holder 中 this 指標
public final void set(T value) {
super.set(value);
if (null == value) { // may set null to remove value
removeValue();
} else {
addValue();
}
}
void removeValue() {
holder.get().remove(this);
}
個人感覺是在inheritThreadlocal裡面在加入一個inheritThreadLocal變數對Threadlocals變數進行封裝,保證執行緒安全
工作流程簡介
自定義TtlRunnable實現Runnable,TtlRunnable初始化方法中保持當前執行緒中已有的TransmittableThreadLocal
private TtlRunnable(Runnable runnable, boolean releaseTtlValueReferenceAfterRun) {
this.copiedRef = new AtomicReference<Map<TransmittableThreadLocal<?>, Object>>(TransmittableThreadLocal.copy());
this.runnable = runnable;
this.releaseTtlValueReferenceAfterRun = releaseTtlValueReferenceAfterRun;
}
執行緒池中執行緒呼叫run方法,執行前先backup holder中所有的TransmittableThreadLocal, copiedRef中不存在,holder存在的,說明是後來加進去的,remove掉holder中的;將copied中的TransmittableThreadLocal set到當前執行緒中
public void run() {
Map<TransmittableThreadLocal<?>, Object> copied = copiedRef.get();
if (copied == null || releaseTtlValueReferenceAfterRun && !copiedRef.compareAndSet(copied, null)) {
throw new IllegalStateException("TTL value reference is released after run!");
}
Map<TransmittableThreadLocal<?>, Object> backup = TransmittableThreadLocal.backupAndSetToCopied(copied);
try {
runnable.run();
} finally {
TransmittableThreadLocal.restoreBackup(backup);
}
}
執行後再恢復 backup 的資料到 holder 中(backup中不存在,holder中存在的TransmittableThreadLocal,從holder中remove掉),將 backup 中的 TransmittableThreadLocal set到當前執行緒中
相關文章
- Java 之 ThreadLocal 詳解Javathread
- Java ThreadLocal 使用詳解Javathread
- ThreadLocal原理用法詳解ThreadLocal記憶體洩漏thread記憶體
- Java中的ThreadLocal詳解Javathread
- JUC---ThreadLocal原理詳解thread
- ThreadLocalthread
- ThreadLocal之深度解讀thread
- ThreadLocal 原始碼解讀thread原始碼
- 一張圖解決ThreadLocal圖解thread
- ThreadLocal原始碼解讀thread原始碼
- 執行緒封閉之ThreadLocal原始碼詳解執行緒thread原始碼
- ThreadLocal分析thread
- ThreadLocal解析thread
- ThreadLocal理解thread
- 解析ThreadLocalthread
- ThreadLocal 剖析thread
- ThreadLocal 解析thread
- 理解ThreadLocalthread
- Java ThreadLocal解析Javathread
- ThreadLocal原理分析thread
- 揭祕ThreadLocalthread
- Java - ThreadLocal類Javathread
- ThreadLocal原始碼thread原始碼
- ThreadLocal 原理分析thread
- ThreadLocal的使用thread
- 請教ThreadLocalthread
- ThreadLocal關鍵thread
- ThreadLocal刨根問底thread
- 深入理解ThreadLocalthread
- ThreadLocal用法及原理thread
- ThreadLocal原理深入解析thread
- ThreadLocal原始碼解析thread原始碼
- ThreadLocal原始碼分析thread原始碼
- ThreadLocal 原始碼分析thread原始碼
- 細說ThreadLocal(一)thread
- ThreadLocal的介紹thread
- 蔣麗苑ThreadLocalthread
- 深入淺出 ThreadLocalthread