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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java 多线程:InheritableThreadLocal 实现原理

發布時間:2025/3/21 java 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java 多线程:InheritableThreadLocal 实现原理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

?

介紹 InheritableThreadLocal 之前,假設對 ThreadLocal 已經有了一定的理解,比如基本概念,原理,如果沒有,可以參考:Java 多線程:threadlocal關鍵字。

這里再復習下 ThreadLocal 的原理,因為會對 InheritableThreadLocal 的理解 有重大的幫助:

  • 每個線程都有一個 ThreadLocalMap 類型的 threadLocals 屬性。
  • ThreadLocalMap 類相當于一個Map,key 是 ThreadLocal 本身,value 就是我們的值。
  • 當我們通過?threadLocal.set(new Integer(123));?,我們就會在這個線程中的 threadLocals 屬性中放入一個鍵值對,key 是 這個 threadLocal.set(new Integer(123)); 的 threadlocal,value 就是值。
  • 當我們通過?threadlocal.get()?方法的時候,首先會根據這個線程得到這個線程的 threadLocals 屬性,然后由于這個屬性放的是鍵值對,我們就可以根據鍵 threadlocal 拿到值。 注意,這時候這個鍵 threadlocal 和 我們 set 方法的時候的那個鍵 threadlocal 是一樣的,所以我們能夠拿到相同的值。
  • Ps:如果這個原理沒搞清楚,那么下文估計有比較難理解,所以建議完完全全搞懂這個原理。

    InheritableThreadLocal 概念

    從上面的介紹我們可以知道,我們其實是根據 Thread.currentThread(),拿到該線程的 threadlocals,從而進一步得到我們之前預先 set 好的值。那么如果我們新開一個線程,這個時候,由于 Thread.currentThread() 已經變了,從而導致獲得的 threadlocals 不一樣,我們之前并沒有在這個新的線程的 threadlocals 中放入值,那么我就再通過 threadlocal.get()方法 是不可能拿到值的。例如如下代碼:

    public class Test {public static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>();public static void main(String args[]){threadLocal.set(new Integer(123));Thread thread = new MyThread();thread.start();System.out.println("main = " + threadLocal.get());}static class MyThread extends Thread{@Overridepublic void run(){System.out.println("MyThread = " + threadLocal.get());}} }

    輸出是:

    main = 123 MyThread = null

    那么這個時候怎么解決??InheritableThreadLocal 就可以解決這個問題。?先看一個官方對它的介紹:

    * This class extends <tt>ThreadLocal</tt> 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 <tt>childValue</tt>* method in this class.

    也就是說,我們把上面的

    public static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>();

    改成

    public static ThreadLocal<Integer> threadLocal = new InheritableThreadLocal<Integer>();

    再運行,就會有結果:

    main = 123 MyThread = 123

    也就是子線程或者說新開的線程拿到了該值。那么,這個究竟是怎么實現的呢,key 都變了,為什么還可以拿到呢?

    InheritableThreadLocal 原理

    我們可以首先可以瀏覽下 InheritableThreadLocal 類中有什么東西:

    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);} }

    其實就是重寫了3個方法。

    首先,當我們調用 get 方法的時候,由于子類沒有重寫,所以我們調用了父類的 get 方法:

    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(); }

    這里會有一個Thread.currentThread()?,?getMap(t)?方法,所以就會得到這個線程 threadlocals。 但是,由于子類 InheritableThreadLocal 重寫了 getMap()方法,再看上述代碼,我們可以看到:
    其實不是得到 threadlocals,而是得到 inheritableThreadLocals。?inheritableThreadLocals 之前一直沒提及過,其實它也是 Thread 類的一個 ThreadLocalMap 類型的 屬性,如下 Thread 類的部分代碼:

    ThreadLocal.ThreadLocalMap threadLocals = null; ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

    那么,這里看 InheritableThreadLocal 重寫的方法,感覺 inheritableThreadLocals 和 threadLocals 幾乎是一模一樣的作用,只是換了個名字而且,那么究竟 為什么在新的 線程中 通過?threadlocal.get()?方法還能得到值呢?

    這時候要注意 childValue 方法,我們可以看下它的官方說明:

    * Computes the child's initial value for this inheritable thread-local* variable as a function of the parent's value at the time the child* thread is created. This method is called from within the parent* thread before the child is started.

    這個時候,你明白了,是不是在 創建線程的時候做了手腳,做了一些值的傳遞,或者這里利用上了 inheritableThreadLocals 之類的。

    其實,是的:

    • 關鍵在于?Thread thread = new MyThread();
    • 關鍵在于?Thread thread = new MyThread();
    • 關鍵在于?Thread thread = new MyThread();

    這不是一個簡簡單單的 new 操作。當我們 new 一個 線程的時候:

    public Thread() {init(null, null, "Thread-" + nextThreadNum(), 0); }

    然后:

    private void init(ThreadGroup g, Runnable target, String name,long stackSize) {init(g, target, name, stackSize, null); }

    然后:

    private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc) {......if (parent.inheritableThreadLocals != null)this.inheritableThreadLocals =ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);/* Stash the specified stack size in case the VM cares */this.stackSize = stackSize;......}

    這時候有一句 'ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);' ,然后

    static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {return new ThreadLocalMap(parentMap); }

    繼續跟蹤:

    private ThreadLocalMap(ThreadLocalMap parentMap) {Entry[] parentTable = parentMap.table;int len = parentTable.length;setThreshold(len);table = new Entry[len];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) {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++;}}}}

    當我們創建一個新的線程的時候X,X線程就會有 ThreadLocalMap 類型的 inheritableThreadLocals ,因為它是 Thread 類的一個屬性。

    然后

    先得到當前線程存儲的這些值,例如?Entry[] parentTable = parentMap.table;?。再通過一個 for 循環,不斷的把當前線程的這些值復制到我們新創建的線程X 的inheritableThreadLocals 中。就這樣,就ok了。

    那么這樣會有一個什么結果呢?

    結果就是我們創建的新線程X 的inheritableThreadLocals 變量中已經有了值了。那么我在新的線程X中調用threadlocal.get()?方法,首先會得到新線程X 的 inheritableThreadLocals,然后,再根據threadlocal.get()中的 threadlocal,就能夠得到這個值。

    這樣就避免了 新線程中得到的 threadlocals 沒有東西。之前就是因為沒有東西,所以才拿不到值。

    所以說 整個 InheritableThreadLocal 的實現原理就是這樣的。

    總結

  • 首先要理解 為什么 在 新線程中得不到值,是因為我們其實是根據 Thread.currentThread(),拿到該線程的 threadlocals,從而進一步得到我們之前預先 set 好的值。那么如果我們新開一個線程,這個時候,由于 Thread.currentThread() 已經變了,從而導致獲得的 threadlocals 不一樣,我們之前并沒有在這個新的線程的 threadlocals 中放入值,那么我就再通過 threadlocal.get()方法 是不可能拿到值的。
  • 那么解決辦法就是 我們在新線程中,要把父線程的 threadlocals 的值 給復制到 新線程中的 threadlocals 中來。這樣,我們在新線程中得到的 threadlocals 才會有東西,再通過?threadlocal.get()?中的 threadlocal,就會得到值。
  • 參考

    • java concurrency in practice讀書筆記---ThreadLocal原理
    • ThreadLocal和synchronized的區別?

    總結

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

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