ThreadLocal

所謂ThreadLocal,就是每個線程人手一隻筆,線程本地存儲。
1、使用
ThreadLocal的常規用法是get和set方法,比如可能這樣用

static  ThreadLocal<Object> tlObj = new ThreadLocal<>();
            //如下使用方法
            if(tlObj .get() == null){
                 tlObj .set(new Object());
            }

ThreadLocal的使用時在應用層面(即代碼層面)自己來實現相關的控制。

2、原理
Thread有個內部有個ThreadLocalMap的變量,是一個用來存儲本線程的ThreadLocal值的Map。

//Thread.java
    ThreadLocal.ThreadLocalMap threadLocals = null;

下面來看下,我們代碼中的get和set方法如何實現。
get方法,實現如下。

//ThreadLocal.java
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();
    }

其中,getMap方法如下,即返回線程的threadLocals變量

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

get方法的核心就是在當前Thread的threadLocals中尋找以當前ThreadLocal對象爲Key存儲的T。

再來看看set方法:

//ThreadLocal.java
    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相反的操作,即在當前線程變量Thread的threadLocals中以當前ThreadLocal對象爲key存儲ThreadLocal的泛型T。

3、再深入一點
Thread對象中的threadLocals是一個ThreadLocalMap,它的每一個Entry結構如下

static class Entry extends WeakReference<ThreadLocal<?>> {
            Object value;

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

這是一個弱引用,所謂弱引用,即不影響java虛擬機進行垃圾回收的引用。如果ThreadLocal的強引用被回收後,ThreadLocalMap的key就會變爲null,這樣系統在ThreadLocalMap清理時,會將這些垃圾數據回收(執行時機不確定)。在線程退出時,會做如下操作。

//Thread.java
private void exit() {
//省略
    threadLocals = null;
//省略
}

將threadLocals置空,會加速threadLocals的回收。
另外,如果需要父子線程進行數據的傳遞,還有一種做法是使用InheritableThreadLocal。
在線程創建時,會將父線程的該部分進行淺拷貝(即引用賦值),所以注意,如果子線程直接修改了引用,父線程是感知不到的,如果是修改了引用的對象內部的值,父線程還是能感知到的。

//Thread.java
//這東西其實就是InheritableThreadLocal的Map,專門用來實現父子線程之間的傳遞
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; 

//線程初始化時做了如下動作
private void init(
//此處省略一羣變量
if (parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
            //繼承它老爹的家產,淺拷貝
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

就醬,繼承遺產這貨,還是不再展開了,還有GC也不細講,都以後展開。

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