Java 線程隔離ThreadLocal

有關Java線程隔離方面的問題也是面試中必問的一個問題,今天就好好看一下ThreadLocal 實現原理,也是我們在解決多線程數據安全時比較常用的一個手段

實現理論

將變量保存爲每個線程棧內部的變量,因爲線程棧與線程棧之間內部的變量不不會相互影響的,所以就不會存在數據不安全的情況。

代碼實現

Thread類

ThreadLocal.ThreadLocalMap threadLocals = null;

Thread內由此Map 負責維護 Thread 內被隔離的變量

ThreadLocalMap

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

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

此Map中的key爲 ThreadLocal對象,value爲存在Thread被隔離變量的值。

ThreadLocal

當使用ThreadLocal set值的時候

    public void set(T value) {
        //獲取當前操作線程對象
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            //將值 放置在 當前線程局部變量 ThreadLocalMap 中
            map.set(this, value);
        else
            createMap(t, value);
    }
    ThreadLocalMap getMap(Thread t) {
        //獲取當前線程局部變量 ThreadLocalMap 
        return t.threadLocals;
    }

至此可以發現達到了 線程隔離的效果

當使用 ThreadLocal get值的時候

    public T get() {
        //獲取當前線程對象
        Thread t = Thread.currentThread();
        //獲取當前線程局部變量 ThreadLocalMap 
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            //從Map中 根據 threadlocal key及this 獲取value
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

小結

至此大概可以總結出Thread ThreadLocal ThreadLocalMap 之間的關係

Thread 中存在 ThreadLocalMap 的強引用,ThreadLocalMap 中 Entry 存在 key 爲 ThreadLocal 的弱引用,以及任意類型 value 的強引用

此處就會出現 內存泄漏 的情況,當一次GC過後,ThreadLocal 會被回收掉變爲null,所以ThreadLocalMap 中的key變爲null,此時value理應被回收掉,但是如果線程對象沒有結束的話,就會存在 Thread -> ThreadLocalMap -> Entry -> (null,value) 的引用鏈,導致value不會被回收,爲了應對這種情況的發生,ThreadLocal 在get 和 set 操作時會將key爲null的 Entry 的 value 也變爲 null

  private int expungeStaleEntry(int staleSlot) {
            Entry[] tab = table;
            int len = tab.length;

            // expunge entry at staleSlot
            tab[staleSlot].value = null;
            tab[staleSlot] = null;
            size--;

            // Rehash until we encounter null
            Entry e;
            int i;
            for (i = nextIndex(staleSlot, len);
                 (e = tab[i]) != null;
                 i = nextIndex(i, len)) {
                //如果key爲null,則將對應的value也變爲null,同時將長度減一
                ThreadLocal<?> k = e.get();
                if (k == null) {
                    e.value = null;
                    tab[i] = null;
                    size--;
                } else {
             //省略部分代碼
                }
            }
            return i;
        }

總結

  1. Thread -> ThreadLocal.ThreadLocalMap -> Entry -> (weakReferencr<ThreadLocal>,T value)
  2. 在 ThreadLocalMap set ,get 時 會執行 key爲null 的 Entry 的 value 也變爲null ,同時減少Map長度。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章