併發編程之詳解InheritableThreadLocal類原理

【本文版權歸微信公衆號"代碼藝術"(ID:onblog)全部,如果轉載請務必保留本段原創聲明,違者必究。】算法

在Java併發編程中,InheritableThreadLocal 與 ThreadLocal 均可以用於線程間通訊,不一樣的是 InheritableThreadLocal 繼承了 ThreadLocal,而且擴展了 ThreadLocal。使用類 InheritableThreadLocal 可以使子線程繼承父線程的值。相反,類 ThreadLocal 不能實現值繼承。編程

使用示例:數組

public class LocalThread extends Thread {
    private static InheritableThreadLocal local = new InheritableThreadLocal();

    @Override
    public void run() {
        System.out.println("thread線程:"+ local.get());
    }

    public static void main(String[] args) throws InterruptedException {
        local.set("main的值");
        LocalThread t = new LocalThread();
        t.start();
        System.out.println("main線程:"+ local.get());
    }

}

分析下 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.
     *
     * @param t the current thread
     */
    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }
 
    /**
     * Create the map associated with a ThreadLocal.
     *
     * @param t the current thread
     * @param firstValue value for the initial entry of the table.
     */
    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }
}

能夠看到,getMap() 方法和 creatMap() 方法都是重寫的 ThreadLocal 類方法,區別在於把 ThreadLocal 中的 threadLocals 換成了 inheritableThreadLocals,這兩個變量都是ThreadLocalMap類型,而且都是Thread類的屬性,源碼以下:併發

/* 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 如何實現值繼承的呢?繼續看下面的代碼:ide

/**
 * 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) {
            @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++;
            }
        }
    }
}

在構造方法的完整源代碼算法中能夠發現,子線程將父線程中的 table 對象以複製的方式賦值給子線程的 table 數組,這個過程是在建立 Thread 類對象時發生的,也就說明當子線程對象建立完畢後,子線程中的數據就是主線程中舊的數據,主線程使用新的數據時,子線程仍是使用舊的數據,由於主子線程使用兩個 Entry[] 對象數組各自存儲本身的值。this

這部分涉及到 Java 的值傳遞。對於對象來講,值的內容實際上是對象的引用。當在父線程中修改對象的某一屬性,子線程因爲引用着相同對象,因此能夠感知到,本質上是在操做同一塊內存地址。線程

對於基本數據類型(int、long)來講,因爲傳遞的是值,在父線程改變了數據後,子線程依舊使用的是舊的數據。這裏尤爲要提 String 字符串,String 雖然不是基本數據類型,可是因爲內部字符數組被 final 修飾帶來的不可變型,當父線程修改其 String 類型數據時,等於替換掉該 String 對象,而並非修改原 String 對象的值,因此子線程依舊不會發生變化。對象

另外,重寫類 InheritableThreadLocal 的 childValue() 方法能夠對繼承的值進行加工,好比經過調用clone() 方法返回 parentValue 的淺拷貝,以達到子線程沒法影響父線程的目的。blog

代碼以下:

public class Local extends InheritableThreadLocal {

    @Override
    protected Object initialValue() {
        return new Date();
    }

    @Override
    protected Object childValue(Object parentValue) {
        return parentValue+"[子線程加強版]";  // parentValue.clone();
    }
}

版權聲明

【本文版權歸微信公衆號"代碼藝術"(ID:onblog)全部,如果轉載請務必保留本段原創聲明,違者必究。!】

相關文章
相關標籤/搜索