InheritableThreadLocal的實現原理

InheritableThreadLocal實現原理java

ThreadLocal 實現原理 : https://my.oschina.net/xinxingegeya/blog/297192ide

來回顧一下 ThreadLocal 實現原理:this

  1. 每一個線程都有 一個 ThreadLocalMap 類型的 threadLocals 屬性。
  2. ThreadLocalMap 類至關於一個Map,key 是 ThreadLocal 自己,value 就是咱們的值。
  3. 當咱們經過 threadLocal.set(new Integer(123)); ,咱們就會在這個線程中的 threadLocals 屬性中放入一個鍵值對,key 是 這個 threadLocal.set(new Integer(123)); 的 threadlocal,value 就是值。
  4. 當咱們經過 threadlocal.get() 方法的時候,首先會根據這個線程獲得這個線程的 threadLocals 屬性,而後因爲這個屬性放的是鍵值對,咱們就能夠根據鍵 threadlocal 拿到值。 注意,這時候這個鍵 threadlocal 和 咱們 set 方法的時候的那個鍵 threadlocal 是同樣的,因此咱們可以拿到相同的值。

 

ThreadLocal 存在的問題

從上面的介紹咱們能夠知道,咱們實際上是根據 Thread.currentThread(),拿到該線程的 threadlocals,從而進一步獲得咱們以前預先 set 好的值。那麼若是咱們新開一個線程,這個時候,因爲 Thread.currentThread() 已經變了,從而致使得到的 threadlocals 不同,咱們以前並無在這個新的線程的 threadlocals 中放入值,那麼我就再經過 threadlocal.get()方法 是不可能拿到值的。例如以下代碼:spa

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{
        @Override
        public void run(){
            System.out.println("MyThread = " + threadLocal.get());
        }
    }
}

輸出是:.net

main = 123線程

MyThread = nullcode

那麼這個時候怎麼解決? InheritableThreadLocal 就能夠解決這個問題。先看一個官方對它的介紹:orm

* This class extends <tt>ThreadLocal</tt> to provide inheritance of valuesblog

* from parent thread to child thread: when a child thread is created, theci

* 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> {
    /**
     * 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.
     * <p>
     * This method merely returns its input argument, and should be overridden
     * if a different behavior is desired.
     *
     * @param parentValue the parent thread's value
     * @return the child thread's initial value
     */
    protected T childValue(T parentValue) {
        return parentValue;
    }

    /**
     * Get the map associated with a ThreadLocal.
     */
    ThreadLocalMap getMap(Thread t) {
        return t.inheritableThreadLocals;
    }

    /**
     * Create the map associated with a ThreadLocal.
     */
    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();

這不是一個簡簡單單的 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);
    ......
}

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

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;
    int len = parentTable.length;
    setThreshold(len);
    table = new Entry[len];

    for (int j = 0; j < len; j++) {
        Entry e = parentTable[j];
        if (e != null) {
            ThreadLocal key = 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 的實現原理就是這樣的。

===============END===============

相關文章
相關標籤/搜索