ThreadLocal 剖析


  1. ThreadLocal是什麼?ThreadLocal有什麼好處?
  2. ThreadLocal會產生內存泄漏嗎?
  3. 當存儲Value的時候發生衝突怎麼辦?
  4. 爲何在ThreadLocalMap中弱引用Entry呢?

  • 官方描述: 該類提供了線程局部 (thread-local) 變量。這些變量不一樣於它們的普通對應物,由於訪問某個變量(經過其 get 或 set 方法)的每一個線程都有本身的局部變量,它獨立於變量的初始化副本。ThreadLocal 實例一般是類中的 private static 字段,它們但願將狀態與某一個線程(例如,用戶 ID 或事務 ID)相關聯。java

  • 本身的理解 ThreadLocal線程本地變量。爲每一個線程建立一個變量副本。 TODO面試

內部結構

  • ThreadLocal由ThreadLocalMap<ThreadLocal,Object>組成,key爲ThreadLocal;
  • 本質上:ThreadLocalMap包含了一個Entry數組;

關鍵方法

1. ThreadLocal#set()

public void set(T value) {
        Thread t = Thread.currentThread();
        //重要:根據線程獲取Map
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

複製代碼

根據當前線程獲取Map,若是Map爲空,則createMap().數據庫

  • ThreadLocal#getMap()作了那些事情呢?
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;//返回一個**線程綁定**的ThreadLocalMap
    }
複製代碼
  • ThreadLocal#createMap()作了那些事情呢?
void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
複製代碼

能夠看出,其實Thread自己就內置了一個ThreadLocalMap。編程

那麼接下來的重點就是看ThreadLocalMap如何初始化了。數組

1.1 建立ThreadLocalMap過程(初始化過程)

ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            //初始化一個長度=16的對象數組。Entry extends WeakReference
            table = new Entry[INITIAL_CAPACITY];
            //肯定Hash值
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            //設值
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
    }
複製代碼

1.2 構造函數,建立Entry並設值

(重要!因此用截圖) bash

image_1cnrgn0mi1ceqoch1gprpkv1i0738.png-100kB

拓展:併發

  1. 爲何在ThreadLocalMap中弱引用Entry呢?
  2. 可是隻有Key是弱引用的,當發生下一次GC時,

1.3 如何設值呢?

麻煩點:若是Hash衝突了怎麼辦呢?函數

private void set(ThreadLocal<?> key, Object value) {
            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);

            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get();
                //
                if (k == key) {
                    e.value = value;
                    return;
                }

                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }
複製代碼

解決方法: ThreadLocalMap解決Hash衝突的方式就是簡單的步長加1或減1,尋找下一個相鄰的位置。ui

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

應用場景

數據庫鏈接、Session管理this

參考

  1. ThreadLocal-面試必問深度解析
  2. 【Java併發編程】深刻分析ThreadLocal(八)
相關文章
相關標籤/搜索