ThreadLocal解析:父線程的本地變量不能傳遞到子線程詳解

    衆所周知,ThreadLocal類是java提供線程本地變量的工具類。但父線程的本地變量卻不能被子線程使用,代碼以下:java

 1 public static void main(String[] args) {
 2         ThreadLocal<String> threadLocal = new ThreadLocal<>();
 3         threadLocal.set("abc");
 4         System.out.println("父線程:"+threadLocal.get());
 5         Thread t1 = new Thread(new Runnable() {
 6             @Override
 7             public void run() {
 8                 System.out.print("子線程:"+threadLocal.get());
 9             }
10         });
11         t1.start();
12     }

    運行結果以下:ide

     至於緣由呢,得先了解ThreadLocal存儲的變量是怎麼存儲的。首先,讓咱們先看看Thread類的源碼:在thread類中有聲明這麼一個成員變量——threadLocals 工具

ThreadLocal.ThreadLocalMap threadLocals = null;

     根據定義能夠看出,這是ThreadLocal類裏的靜態內部類,它的結構是Map結構,以鍵值對的形式存儲值。key值就是當前ThreadLocal類的實例,value值就是當前線程的本地變量。因此線程的本地變量是存在線程實例當中的,而不是存在ThreadLocal中。ThreadLocal只是一個工具類,體如今當ThreadLocal實例調用set()方法時,會將當前線程的threadLocals變量實例化。如下是ThreadLocal類的set()方法源碼。this

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

    能夠看到,先是獲取當前線程的實例,再獲取線程的成員變量threadLocals,若是threadLocals已經實例化,就直接以當前ThreadLocal類爲key,存儲的本地變量爲value,存進threadLocals中。因此線程本地變量是存儲在線程中的。若是threadLocals未實例化,則調用createMap()方法,該方法會調用ThreadLocalMap的構造方法,初始化一個以當前ThreadLocal類爲key,存儲的本地變量爲value的Map。該Map是相似HashMap的,感興趣的話能夠繼續往下看源碼,此處不作擴展。spa

    回到最開始的問題,子線程調用ThreadLocal類的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();
    }

    此時,會產生兩個分支,一個是經過子線程的ThreadLocalMap的getEntry()方法獲取Entry並返回value值,但因爲子線程的ThreadLocalMap!=父線程的ThreadLocalMap,因此獲取不到父線程的本地變量。另外一個分支是返回setInitialValue()的返回值,setInitialValue()方法源碼以下:code

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;
    }

    能夠看到value會默認爲null,若是子線程的ThreadLocalMap已經實例化,則直接以ThreadLocal實例爲key,null爲value存進ThreadLocalMap中,不然就建立一個同上所述的ThreadLocalMap。blog

    引起出的問題:ThreadLocal類是弱引用,一次GC後會爲null,當key爲null時,value值卻還存在內存中,形成內存泄漏。因此ThreadLocal類最後必定要執行remove()方法。在GC以前將內存釋放。內存

    綜上,第一次寫博客,寫得很差或者有錯誤的地方,請指正。rem

相關文章
相關標籤/搜索