《jdk源碼解析-java中的ThreadLocal類》首發橙寂博客轉發請加此提示html
關於ThreadLocal這個類,應該不少人都使用過。咱們能夠認爲這是一個操做線程間局部對象的工具類。java
首先咱們看下官方解釋spring
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).
官方的意思大概是:
此類提供線程局部變量。 這些變量不一樣於
它們的正常對應部分是,每一個線程(經過
{@code get}或{@code set}方法)get/set有本身的,線程間互不影響的獨立初始化的
變量的副本。 {@code ThreadLocal}實例一般是私有的
但願將狀態與線程相關聯的類中的靜態字段(例如,
用戶ID或交易ID)。api
爲何我把ThreadLocal
做爲了一個工具類存在?在咱們的使用用咱們常常使用get
跟set
方法。就能在當前線程下取得或設置一個value。事實上這個對象也並
沒有不少騷東西也就是一個入口而已,以及負責維護Thread
跟ThreadLocalMap
的一些關係,可是看本文的關鍵點也是理清ThreadLocalMap
,ThreadLocal
,Thread
,Entry
之間的關係。數組
下面簡單看一下ThreadLocal
的get與set方法。數據結構
/** * Sets the current thread's copy of this thread-local variable * to the specified value. Most subclasses will have no need to * override this method, relying solely on the {@link #initialValue} * method to set the values of thread-locals. * * @param value the value to be stored in the current thread's copy of * this thread-local. */ public void set(T value) { //獲取當前線程中ThreadLocalMap對象 Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else //在當前線程下建立一個map並設置value createMap(t, value); } /** * Returns the value in the current thread's copy of this * thread-local variable. If the variable has no value for the * current thread, it is first initialized to the value returned * by an invocation of the {@link #initialValue} method. * * @return the current thread's value of this thread-local */ public T get() { //得到當前線程 Thread t = Thread.currentThread(); //得到當前線程的ThreadLocalMap對象 ThreadLocalMap map = getMap(t); if (map != null) { //獲取map中的具體存取的數據。經過ThreadLocal對象實例。 ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; //建立一個map設置一個默認值。 return setInitialValue(); }
由上能夠看出
ThreadLocal負責維護Thread
跟ThreadLocalMap
的關係。ThreadLocal
是做爲ThreadLocalMap
key存在的負責存取數據的是ThreadLocalMap
。具體存取的數據結構
是ThreadLocalMap
中的Entry
類。多線程
ThreadLocalMap
是ThreadLocal
的內置靜態類。Thread
中內置了一個ThreadLocalMap
的屬性threadLocals默認是爲null的。ThreadLocalMap
內置了Entry
類。ThreadLocalMap中內置了一個關鍵屬性
Entry類型的數組tables。tables是用來存具體數據的。在
ThreadLocalMap`中。數組的下標是以ThreadLocal
中的threadLocalHashCode值做爲下標的。這裏threadLocalHashCode是原子性的ide
Entry
類擴展了WeakReference
(弱引用)這一點記住要考的(詳情看下面代碼)。其中只內置了一個Object
類型的value屬性。* ThreadLocalMap is a customized hash map suitable only for * maintaining thread local values. No operations are exported * outside of the ThreadLocal class. The class is package private to * allow declaration of fields in class Thread. To help deal with * very large and long-lived usages, the hash table entries use * WeakReferences for keys. However, since reference queues are not * used, stale entries are guaranteed to be removed only when * the table starts running out of space. */ static class ThreadLocalMap { /** * The entries in this hash map extend WeakReference, using * its main ref field as the key (which is always a * ThreadLocal object). Note that null keys (i.e. entry.get() * == null) mean that the key is no longer referenced, so the * entry can be expunged from table. Such entries are referred to * as "stale entries" in the code that follows. */ //繼承WeakReference保證了key必定要爲ThreadLocal對象,若是ThreadLocal被回收了。ThreadLocalMap也會回收並不會報錯。 //這裏建議看一下強引用與弱引用的區別 static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } } /** * The initial capacity -- MUST be a power of two. */ //默認數組大寫爲16擴容建議*2 private static final int INITIAL_CAPACITY = 16; /** * The table, resized as necessary. * table.length MUST always be a power of two. */ //具體存數據的table private Entry[] table; /** * The number of entries in the table. */ //存取的條目 private int size = 0; /** * The next size value at which to resize. */ //下一個要擴容的大小 private int threshold; // Default to 0 }
由以上設計咱們就能分析出,一個線程能存多個不一樣ThreadLocal
來管理的對象。一個ThreadLocal
只能對應一個線程的ThreadLocalMap
。因爲存取的
數據結構的對應關係是以ThreadLocal
的hash值來對應的。因此不一樣線程間的對象都是獨立的。工具
ThreadLocalMap
的getEntry方法/** * Get the entry associated with key. This method * itself handles only the fast path: a direct hit of existing * key. It otherwise relays to getEntryAfterMiss. This is * designed to maximize performance for direct hits, in part * by making this method readily inlinable. * * @param key the thread local object * @return the entry associated with key, or null if no such */ //經過ThreadLocal得到了value的值 //若是獲取不到那麼會調轉到getEntryAfterMiss這個方法 private Entry getEntry(ThreadLocal<?> key) { int i = key.threadLocalHashCode & (table.length - 1); Entry e = table[i]; if (e != null && e.get() == key) return e; else return getEntryAfterMiss(key, i, e); } /** * Version of getEntry method for use when key is not found in * its direct hash slot. * * @param key the thread local object * @param i the table index for key's hash code * @param e the entry at table[i] * @return the entry associated with key, or null if no such */ private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) { Entry[] tab = table; int len = tab.length; while (e != null) { ThreadLocal<?> k = e.get(); if (k == key) return e; if (k == null) //清空插槽 expungeStaleEntry(i); else i = nextIndex(i, len); e = tab[i]; } return null; }
ThreadLocalMap
的set方法/** * Set the value associated with key. * * @param key the thread local object * @param value the value to be set */ private void set(ThreadLocal<?> key, Object value) { // We don't use a fast path as with get() because it is at // least as common to use set() to create new entries as // it is to replace existing ones, in which case, a fast // path would fail more often than not. Entry[] tab = table; int len = tab.length; int i = key.threadLocalHashCode & (len-1); //循環去查詢已經存在的entry //若是key相同就替換 //若是key爲null就清空掉 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
的remove方法這個方法使用完get方法後,假若不在用到要記得使用,在多線程環境下,若是使用完後不remove掉很贊成內存溢出。post
/** /** * Remove the entry for key. */ private void remove(ThreadLocal<?> key) { Entry[] tab = table; int len = tab.length; int i = key.threadLocalHashCode & (len-1); //找到key相同而後把value設置爲null for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { if (e.get() == key) { //把value清空 e.clear(); expungeStaleEntry(i); return; } } }
本文從與ThreadLocal
相關的ThreadLocalMap
,Thread
,Entry
等幾個類開始分析。從數據結構以及經常使用api分析得出:具體的數據是存在Thread
類中的ThreadLocalMap
類型中的
threadLocals中。具體的數據是存在ThreadLocalMap
中Entry
類型中的tables數組中。其中ThreadLocal
是做爲key而存在。從而達到了ThreadLocal
管理多個線程且不互相沖突。
ThreadLocal
在spring5.2的版本中是被拋棄了,這個類若是使用不當很容易致使內存溢出。其中就涉及到了爲何說使用完後要主動remove以及entry中爲何會使用弱引用。