日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

InheritableThreadLocal父子线程变量共享实现原理

發布時間:2025/6/17 编程问答 15 如意码农
生活随笔 收集整理的這篇文章主要介紹了 InheritableThreadLocal父子线程变量共享实现原理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

概述

??我們知道ThreadLocal 的設計初衷是為了解決多線程并發導致的線程安全問題,向每一個線程提供一個自己的變量副本,實現變量隔離。那如何在不同線程之間共享變量呢?InheritableThreadLocal為解決此問題而生,使用她可以實現父子線程訪問ThreadLocal的值。

??實現變量傳遞的關鍵是在類Thread中定義的本地變量inheritableThreadLocals:

    /* 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;

案例分析

??下面先提供一個案例,說明InheritableThreadLocal可以實現父子線程間傳遞變量。

/**
* 線程間共享變量
*
* @author Wiener
* @date 2020/10/27
*/
public class ShareArgsProblem {
private static void shareArgs() {
//主線程中賦值
ThreadLocal<String> threadLocal = new ThreadLocal<>();
final ThreadLocal itl = new InheritableThreadLocal();
itl.set("帥得一匹!");
threadLocal.set("在 ThreadLocal 中存放了值");
System.out.println(String.format("① 當前線程名稱:%s", Thread.currentThread().getName()));
Thread t = new Thread() {
@Override
public void run() {
super.run();
//子線程中讀取
System.out.println(String.format("樓蘭的胡楊帥么?%s", itl.get()));
System.out.println(String.format("拿不到ThreadLocal 存儲的值:%s", threadLocal.get()));
System.out.println(String.format("② 當前線程名稱: %s", Thread.currentThread().getName()));
}
};
t.start();
} public static void main(String[] args) {
shareArgs();
}
}

??執行main函數,輸出結果如下:

??在上面的測試用例中,開啟兩個線程——主線程和子線程t,主線程中創建了兩個變量和一個新的線程t,并嘗試在線程t中獲取本地變量的值。從輸出結果可以看得出來,我們拿不到ThreadLocal類型變量 threadLocal 的值,但是,能夠準確拿到 InheritableThreadLocal 類型的變量 itl的值。下面分析為什么可以讀取出來InheritableThreadLocal 類型的變量的值。

InheritableThreadLocal共享變量原理分析

??先讓我們看看InheritableThreadLocal的寥寥數行源碼:


/**
* This class extends {@code ThreadLocal} to provide inheritance of values
* from parent thread to child thread: when a child thread is created, the
* child receives initial values for all inheritable thread-local variables
* for which the parent has values. Normally the child's values will be
* identical to the parent's; however, the child's value can be made an
* arbitrary function of the parent's by overriding the {@code childValue}
* method in this class.
*
* <p>Inheritable thread-local variables are used in preference to
* ordinary thread-local variables when the per-thread-attribute being
* maintained in the variable (e.g., User ID, Transaction ID) must be
* automatically transmitted to any child threads that are created.
* @author Josh Bloch and Doug Lea
*/ public class InheritableThreadLocal<T> extends ThreadLocal<T> {
protected T childValue(T parentValue) {// 值傳遞
return parentValue;
}
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}

??類InheritableThreadLocal繼承自 ThreadLocal,作為子類,它重寫了如上所述的三個方法。從上述注釋得知,父線程thread-local 變量會被傳遞至子線程中,下面看看如何傳遞的。


public Thread() {//創建新線程時調用的構造方法
this(null, null, "Thread-" + nextThreadNum(), 0);
}
public Thread(ThreadGroup group, Runnable target, String name,
long stackSize) {
this(group, target, name, stackSize, null, true);
}
/**
* Initializes a Thread.
*/
private Thread(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
...
Thread parent = currentThread();
...
setPriority(priority);
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);//①
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize; /* Set thread ID */
this.tid = nextThreadID();
}

??跟著進去幾層后,發現在①處給inheritableThreadLocals賦值,繼續看 ThreadLocal.createInheritedMap的返回值是什么:

    static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
/**
* Construct a new map including all Inheritable ThreadLocals
* from given parent map. Called only by createInheritedMap.
*
* @param parentMap the map associated with parent thread.
*/
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table; //父線程的table
int len = parentTable.length;
setThreshold(len);
table = new Entry[len];//當前線程的table
//循環取父線程的值到當前線程
for (Entry e : parentTable) {
if (e != null) {
@SuppressWarnings("unchecked")
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if (key != null) {
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++;
}
}
}
}

??從構造方法ThreadLocalMap可以得知,首先拿到父線程中的鍵值對表table,然后循環遍歷parentTable,把父線程中的這些值通過值傳遞復制到子線程的ThreadLocalMap對象中,此時子線程的成員變量 inheritableThreadLocals中就有了值。其實,ThreadLocalMap的注釋已經明確告訴我們,這里將把父線程的可遺傳本地變量(Inheritable ThreadLocals)復制到當前線程中。這種模式,有點類似于ClassLoader的 loadClass() 的機制。

??通過查看Object value = key.childValue(e.value)的實現方式,我們知道這里是值傳遞。

小結

??Thread對象通過成員變量ThreadLocal.ThreadLocalMap inheritableThreadLocals維護從父線程(創建該線程的線程)繼承而來的數據。原理就是在父線程創建子線程時,如果父線程的inheritableThreadLocals不是null,則ThreadLocalMap中的構造函數會復制一份保存到子線程的inheritableThreadLocals變量中。

??由于本地變量在父子傳遞過程中通過值傳遞,所以即使父線程的本地變量發生了改變,子線程的本地變量依舊是創建線程時初始化的值。在實際的應用場景里,基本都是使用線程池來進行多線程編程,因線程池復用線程,而非每次創建新的線程,所以如果更新父線程InheritableThreadLocal 的值,被復用的子線程中的InheritableThreadLocal變量值不會被更新,從而導致數據異常。阿里開源的TransmittableThreadLocal 可以解決此問題,感興趣的老鐵們可以去了解一下。

??以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多點贊支持。

Reference

  • https://www.cnblogs.com/noteless/p/10448283.html
  • https://blog.csdn.net/weixin_34221775/article/details/89657201
  • https://it007.blog.csdn.net/article/details/107049975

總結

以上是生活随笔為你收集整理的InheritableThreadLocal父子线程变量共享实现原理的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。