ThreadLocal之我所見

網上有不少關於ThreadLocal的文章,大部分都提到了多線程之間共享資源的問題。其實ThreadLocal和多線程之間一點關係都沒有。若是有,我怕是它的名字改爲ThreadShare是否是更合適呢?開個玩笑。從其名稱ThreadLocal,咱們就能夠看出他應該是隸屬於線程內部的資源。接下來就詳細說說吧。html

咱們之前在處理一個Request請求,穿透多個業務方法的時候,若是要共享數據,通常都會搞個Context上下文對象,在多個方法之間進行傳遞,這樣能夠解決多個方法之間的數據共享問題。這是很不錯的一種方案,並且這種方案在實際使用的時候效果也很是不錯,由於Context內部咱們想放什麼,就放什麼,想怎麼放就怎麼放,很是的自由無界。可是這種自由帶來的問題在小流量的請求中是不會有問題的,可是在大流量的請求中,則存在不小的問題,主要在:安全

1. Context對象,每一個請求進來,都會new一個,大流量下,瞬間暴增,因爲空間申請操做勢必引起頻繁的young GC, 業務壓力大的時候,full GC也是不可避免的。多線程

2. Context對象,在一個請求終結以後,須要手動釋放。ide

3. Context對象,存在被請求內部的多線程共享訪問的情形。有線程安全性問題。this

上面三個問題,是我隨便列舉的,可是在實際使用中,可能還有更多。可是若是Context的生產和銷燬若是控制的足夠好的話,上面的問題也不是什麼問題。spa

既然Context對象的控制稍顯麻煩,那麼JDK有沒有提供什麼現成的類庫供咱們使用呢? 答案是確定的,這個對象就是ThreadLocal對象。線程

說道ThreadLocal對象,我更認爲他是在當前請求的上游和下游之間進行數據共享的。那麼按照以前的例子說來,若是一個請求穿越多個業務方法,其實數據的共享能夠利用ThreadLocal來進行,這樣就無需專門定義一個Context。code

首先來看看其內部get實現機制:htm

/**
 * Returns the value in the current thread's copy of this
 * thread-local variable.  If the variable has no value for the
 * current thread, it is first initialized to the value returned
 * by an invocation of the {@link #initialValue} method.
 *
 * @return the current thread's value of this thread-local
 */
public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null)
            return (T)e.value;
    }
    return setInitialValue();
}

ThreadLocalMap getMap(Thread t) {
    return t.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;
}

protected T initialValue() {
    return null;
}

從上面源碼能夠看出,get操做會從當前Thread上附加的map中進行數據獲取。對象

再來看看set方法:

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

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

從上面源碼看出,首先他會取出當前Thread,而後會將map設置到當前Thread上,用戶能夠在這個map上進行數據增刪改查操做。很是巧妙。因此從這裏能夠看出,一個ThreadLocal對象,即使穿插在多個線程之間,也不會形成資源共享問題,由於他會爲每一個線程都設置map對象,這也從根本上避免了線程安全問題。

最後,由於ThreadLocal內部的對象爲WeakReference,因此不用進行手動釋放,只須要保證一個請求結束,對其內部的引用釋放掉就好了,而後自動會被JVM優先處理掉,根本無需擔憂內存泄露問題。

相關文章
相關標籤/搜索