ThreadLocal源碼解析

* This class provides thread-local variables. These variables differ from * their normal counterparts in that each thread that accesses one (via its * {@code get} or {@code set} method) has its own, independently initialized
* copy of the variable.  {@code ThreadLocal} instances are typically private
* static fields in classes that wish to associate state with a thread (e.g., * a user ID or Transaction ID).複製代碼

ThreadLocal類用來提供線程內部的局部變量。這種變量在多線程環境下訪問(經過get或set方法訪問)時能保證各個線程裏的變量相對獨立於其餘線程內的變量。ThreadLocal實例一般來講都是private static類型的,用於關聯線程和線程的上下文。java

ThreadLocal的做用是提供線程內的局部變量,這種變量在線程的生命週期內起做用,減小同一個線程內多個函數或者組件之間一些公共變量的傳遞的複雜度
緩存

ThreadLocal不是同步機制,也不解決共享對象的多線程競態條件問題。安全

ThreadLocal用來輔助平衡效率與線程的資源分配。bash

ThreadLocal會爲每個線程提供一個獨立的變量副本,從而隔離了多個線程對數據的併發訪問衝突。由於每個線程都擁有本身的變量副本,從而也就沒有必要對該變量進行同步了。多線程

對於多線程資源共享的問題,同步機制採用了「 以時間換空間」的方式,ThreadLocal採用了「 以空間換時間」的方式。同步機制僅提供一份變量,讓不一樣的線程排隊訪問,ThreadLocal爲每個線程都提供了一份變量, 所以能夠同時訪問而互不影響。若是僅在單線程內訪問數據,就不須要同步,這種技術被稱爲 線程封閉(Thread Confinement)。Java語言及其核心庫提供了一些機制來幫助維持線程封閉性,例如ThreadLocal類。

ThreadLocal併發


set(T):保存對象jvm

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}複製代碼

get():當某個線程初次調用ThreadLocal.get方法時,就會調用initialValue()來獲取初始值,不然返回由當前執行線程在調用set(T)的最新值,該方法避免了將這個對象做爲參數傳遞的麻煩。ide

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();
}複製代碼

(1)獲取當前線程
函數

(2)根據當前線程獲取一個map 性能

(3)若是獲取的map不爲空,則在map中以ThreadLocal的引用做爲key來在map中獲取對應的value e,不然轉到(5)

(4)若是e不爲null,則返回e.value,不然轉到(5)

(5)Map爲空或者e爲空,則經過initialValue函數獲取初始值value,而後用ThreadLocal的引用和value做爲firstKey和firstValue建立一個新的Map

remove():將當前線程的ThreadLocal綁定的值刪除

public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null)
        m.remove(this);
}複製代碼

initialValue():用於設置 ThreadLocal 的初始值,默認返回 null,該函數是protected類型的,一般該函數都會以匿名內部類的形式被重載,以指定初始值

protected T initialValue() {
    return null;
}複製代碼

getMap(Thread):獲取線程的ThreadLocalMap

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}複製代碼

createMap(Thread,T):初始化線程的threadLocals,而後設定key-value

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

createInheritedMap(ThreadLocalMap):

static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
    return new ThreadLocalMap(parentMap);
}複製代碼

nextHashCode():

private static int nextHashCode() {
    return nextHashCode.getAndAdd(HASH_INCREMENT);
}複製代碼

setInitialValue():

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;
}複製代碼


JDK1.3:ThreadLocal<T>視爲包含了Map<Thread,T>對象,其中保存了特定於該線程的值。而後用線程的ID做爲Map的key,實例對象做爲Map的value,這樣就能達到各個線程的值隔離的效果

JDK1.8:每一個Thread維護一個ThreadLocalMap映射表,這個映射表的key是ThreadLocal實例自己,value是真正須要存儲的Object。

JDK1.8優點

  1. 每一個Map的Entry數量變小了:以前是Thread的數量,如今是ThreadLocal的數量,能提升性能
  2. 當Thread銷燬以後對應的ThreadLocalMap也就隨之銷燬了,能減小內存使用量



每一個線程中都有一個本身的ThreadLocalMap類對象,能夠將線程本身的對象保持到其中,各管各的,線程能夠正確的訪問到本身的對象。

java.lang.Thread:

ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;複製代碼


static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;

    Entry(ThreadLocal<?> k, Object v) {
        super(k);
        value = v;
    }
}複製代碼

ThreadLocalMap是使用ThreadLocal的弱引用做爲Key的。保存在Thread中,當線程終止後,這些值會做爲 垃圾回收


ThreadLocal不能繼承父線程的ThreadLocal的內容, InheritableThreadLocal類繼承於ThreadLocal類,InheritableThreadLocal變量值會自動傳遞給全部子線程,在父子線程之間傳遞數據。 建立一個線程時若是保存了全部 InheritableThreadLocal 對象的值,那麼這些值也將自動傳遞給子線程。若是一個子線程調用 InheritableThreadLocal 的 get() ,那麼它將與它的父線程看到同一個對象。爲保護線程安全性,您應該只對不可變對象(一旦建立, 其狀態就永遠不會被改變的對象)使用InheritableThreadLocal ,由於對象被多個線程共享。 InheritableThreadLocal合適用於把數據從父線程傳到子線程, 很例如用戶標識(user id)或事務標識(transaction id),但不能是有狀態對象,例如 JDBC Connection 。

private static ThreadLocal<Integer> integerThreadLocal = new ThreadLocal<>();
private static InheritableThreadLocal<Integer> inheritableThreadLocal =
        new InheritableThreadLocal<>();

@Test
public void inheritableThreadLocal() {
    // 父線程
    integerThreadLocal.set(1);
    inheritableThreadLocal.set(1);
    //結果:pool-1-thread-1:null/1
    threadFactory.newThread(() -> System.out.println(Thread.currentThread().getName() + ":"
            + integerThreadLocal.get() + "/"
            + inheritableThreadLocal.get())).start();
}複製代碼

Reference(引用)



  • 強引用(FinalReference、Finalizer )

相似「Object obj =new Object()」這類的引用,只要強引用還存在,垃圾收集器永遠不會回收掉被引用的對象。強引用是Java的默認引用實現, 它會盡量長時間的存活於JVM內, 當沒有任何對象指向它時, GC執行後也不會被回收。若是一個對象具備強引用,那就相似於必不可少的生活用品, 垃圾回收器毫不會回收它。當內存空間不足,Java虛擬機寧願拋出OutOfMemoryError錯誤, 使程序異常終止,也不會靠隨意回收具備強引用的對象來解決內存不足問題。JVM 系統採用 Finalizer 來管理每一個強引用對象 , 並將其被標記要清理時加入 ReferenceQueue, 並逐一調用該對象的 finalize() 方法

Object obj = new Object();
Object strongReference = obj;
Assertions.assertSame(obj, strongReference);
obj = null;
System.gc();
Assertions.assertNull(obj);
Assertions.assertNotNull(strongReference);複製代碼


Finalizer是FinalReference的子類,該類被final修飾,不可再被繼承,JVM實際操做的是Finalizer。當一個類知足實例化FinalReference的條件時,JVM會調用Finalizer.register()進行註冊。

  • 軟引用(java.lang.ref.SoftReference)
用來描述一些還有用,但並不是必需的對象。對於軟引用關聯着的對象,在系統將要發生內存溢出異常以前,將會把這些對象列進回收範圍之中並進行第二次回收。若是此次回收仍是沒有足夠的內存,纔會拋出內存溢出異常。SoftReference 於 WeakReference 的特性基本一致,最大的區別在於 SoftReference 會盡量長的保留引用直到 JVM 內存不足時纔會被回收(虛擬機保證),這一特性使得SoftReference很是適合緩存應用
Object obj = new Object();
SoftReference<Object> softReference = new SoftReference<>(obj);
Assertions.assertNotNull(softReference.get());
obj = null;
System.gc();
Assertions.assertNull(obj);
// SoftReference 只有在 jvm OutOfMemory 以前纔會被回收, 因此它很是適合緩存應用
Assertions.assertNotNull(softReference.get());複製代碼
  • 弱引用(java.lang.ref.WeakReference)


用來描述非必須對象的,但它的強度比軟引用要弱一些,被弱引用關聯的對象只能生存到下一次垃圾收集發生以前。當垃圾收集器工做時,不管當前內存是否足夠,都會回收掉只被弱引用關聯的對象。

Object obj = new Object();
WeakReference<Object> weakReference = new WeakReference<>(obj);
Assertions.assertSame(obj, weakReference.get());
obj = null;
System.gc();
Assertions.assertNull(weakReference.get());

Map<Object, Object> weakHashMap = new WeakHashMap<>();
Object key = new Object();
Object value = new Object();
weakHashMap.put(key, value);
Assertions.assertTrue(weakHashMap.containsValue(value));
key = null;
System.gc();
Thread.sleep(1000);
// 一旦沒有指向 key 的強引用, WeakHashMap 在 GC 後將自動刪除相關的 entry
Assertions.assertFalse(weakHashMap.containsValue(value));複製代碼
  • 虛引用(java.lang.ref.PhantomReference)
稱爲幽靈引用或者幻影引用,是最弱的一種引用關係。一個對象是否有虛引用的存在,徹底不會對其生存時間構成影響,也沒法經過虛引用來取得一個對象實例。爲一個對象設置虛引用關聯的惟一目的就是但願能在這個對象被收集器回收時收到一個系統通知。
相關文章
相關標籤/搜索