Java 源碼解析-ThreadLocal

ThreadLocal相當於提供了一種線程隔離,將變量與線程相綁定,它可以保證訪問到的變量屬於當前線程,每個線程都保存有一個變量副本,每個線程的變量都不同,而同一個線程在任何時候訪問這個本地變量的結果都是一致的。當此線程結束生命週期時,所有的線程本地實例都會被GC。

ThreadLocal 的基本用法

public class ThreadLocal {

    private static java.lang.ThreadLocal<Integer> seqNum = new java.lang.ThreadLocal<Integer>() {
        @Override
        protected Integer initialValue() {
            return 0;
        }
    };

    private int getNextNum() {
        seqNum.set(seqNum.get() + 1);
        return seqNum.get();
    }



    public static void main(String[] args) {
        ThreadLocal sn = new ThreadLocal();
        ThreadId t1 = new ThreadId(sn);
        ThreadId t2 = new ThreadId(sn);
        ThreadId t3 = new ThreadId(sn);

        t1.start();
        t2.start();
        t3.start();
    }

    private static class ThreadId extends Thread {
        private ThreadLocal sn;

        public ThreadId(ThreadLocal sn) {
            this.sn = sn;
        }

        @Override
        public void run() {
            for (int i = 0; i < 3 ; i++) {
                System.out.println("thread[" + Thread.currentThread().getName() + "] --> sn["
                        + sn.getNextNum() + "]");
            }
        }
    }
}

輸出結果

thread[Thread-0] --> sn[1]
thread[Thread-1] --> sn[1]
thread[Thread-1] --> sn[2]
thread[Thread-0] --> sn[2]
thread[Thread-0] --> sn[3]
thread[Thread-1] --> sn[3]
thread[Thread-2] --> sn[1]
thread[Thread-2] --> sn[2]
thread[Thread-2] --> sn[3]

ThreadLocal 源碼

private final int threadLocalHashCode = nextHashCode();

/**
 * The next hash code to be given out. Updated atomically. Starts at
 * zero.
 */
private static AtomicInteger nextHashCode =
    new AtomicInteger();

/**
 * The difference between successively generated hash codes - turns
 * implicit sequential thread-local IDs into near-optimally spread
 * multiplicative hash values for power-of-two-sized tables.
 */
private static final int HASH_INCREMENT = 0x61c88647;

/**
 * Returns the next hash code.
 */
private static int nextHashCode() {
    return nextHashCode.getAndAdd(HASH_INCREMENT);
}

threadLocalHashCode 用來標識ThreadLocal的唯一性,每次hash操作的增量爲0x61c88647。

/**
 * Sets the current thread's copy of this thread-local variable
 * to the specified value.  Most subclasses will have no need to
 * override this method, relying solely on the {@link #initialValue}
 * method to set the values of thread-locals.
 *
 * @param value the value to be stored in the current thread's copy of
 *        this thread-local.
 */
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

通過當前線程t 來獲取 ThreadLocalMap ,調用的方法是 getMap(t), getMap 的代碼如下:

/**
 * Get the map associated with a ThreadLocal. Overridden in
 * InheritableThreadLocal.
 *
 * @param  t the current thread
 * @return the map
 */
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

/* ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;

返回的是Thread 中的 threadLocals 屬性,每個Thread裏面都有一個ThreadLocal.ThreadLocalMap成員變量,也就是說每個線程通過ThreadLocal.ThreadLocalMap與ThreadLocal相綁定,這樣可以確保每個線程訪問到的thread-local variable都是本線程的。

第一次獲取threadLocals 爲空,這個時候調用

private T setInitialValue() {
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
    return value;
}

我們來分析 createMap

/**
 * Create the map associated with a ThreadLocal. Overridden in
 * InheritableThreadLocal.
 *
 * @param t the current thread
 * @param firstValue value for the initial entry of the map
 * @param map the map to store.
 */
void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

下面分析 ThreadLocalMap ,它是ThreadLocal的靜態內部類。

成員變量與常量

/**
 * The initial capacity -- MUST be a power of two.
 */
private static final int INITIAL_CAPACITY = 16;  //Map 的初始容量,必須是2的N次冪

/**
 * The table, resized as necessary.
 * table.length MUST always be a power of two.
 */
private Entry[] table;    //用於存儲數據

/**
 * The number of entries in the table.
 */
private int size = 0; //table 中數據的數量

/**
 * The next size value at which to resize.
 */
private int threshold; // Default to 0  //擴容時對應的閾值

下面分析存儲數據的類Entry 代碼如下 :

/**
 * The entries in this hash map extend WeakReference, using
 * its main ref field as the key (which is always a
 * ThreadLocal object).  Note that null keys (i.e. entry.get()
 * == null) mean that the key is no longer referenced, so the
 * entry can be expunged from table.  Such entries are referred to
 * as "stale entries" in the code that follows.
 */
static class Entry extends WeakReference<ThreadLocal> {
    /** The value associated with this ThreadLocal. */
    Object value;

    Entry(ThreadLocal k, Object v) {
        super(k);
        value = v;
    }
}

Entry類繼承了WeakReference

ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
    table = new Entry[INITIAL_CAPACITY];
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
    table[i] = new Entry(firstKey, firstValue);
    size = 1;
    setThreshold(INITIAL_CAPACITY);
}

第一個參數就是本ThreadLocal實例(this),第二個參數就要保存的Value。構造函數首先創建一個長度爲16的Entry數組,然後計算出firstKey對應的哈希值,然後存儲到table中,並設置size和threshold。

剩下的操作就是對ThreadLocalMap操作,報考set get等

ThreadLocal 的分析到這裏就結束了,下面看看用法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章