說在前面
ThreadLocal
用來提供線程級別變量,變量只對當前線程可見。相比與“使用鎖控制共享變量訪問順序”的解決方案。ThreadLocal
通過空間換時間的方案,規避了競爭問題,因爲每個線程都有屬於自己的變量。
此時就產生了第一個問題:線程如何維護屬於自己的變量副本,搞懂了這個也就搞懂了其原理。
源碼分析
一、首先看下Thread類
Thread
中有一個 threadLocals
屬性表示線程的本地變量。這個屬性的類型是 ThreadLocal.ThreadLocalMap
ThreadLocalMap
是 ThreadLocal
的內部類,他是類 Map
結構,也是存儲 K-V
結構數據,並用 Entry
封裝 K-V
。不同的是 ThreadLocalMap
的 Entry
的 Key
只能是 ThreadLocal
類型對象,並且是一個弱引用。
也就是說線程通過一個類Map數據結構 ThreadLocal.ThreadLocalMap
來存儲屬於自己的線程變量。
三、ThreadLocal本尊
ThreadLocalMap
賦值、取值操作的入口在其外部類 ThreadLocal
中。
set(v)
方法內調用 ThreadLocalMap
的 set(this,v)
方法存值。(類似 Map
的 put(k,v)
方法)
get()
方法內調用 ThreadLocalMap
的 getEntry(this)
方法取值(類似 Map
的 get(k)
方法)
通過代碼可以看出:
-
第一次操作線程的
ThreadLocalMap
屬性時,會初始化一個ThreadLocal.ThreadLocalMap
,set(v)
會存入以參數爲Value
的K/V
數據,get()
會存入以null
爲value
的K/V
數據。 -
ThreadLocal.ThreadLocalMap
存值操作入口是ThreadLocal.set(v)
方法,並以當前ThreadLocal
變量爲key
,參數爲value
。 -
ThreadLocal.ThreadLocalMap
取值操作入口是ThreadLocal.get(v)
方法,key
爲當前ThreadLocal變量。
我們在從代碼層面直觀的體會這個操作:
至此線程的本地變量的本質就清晰了。就是 Thread
用類似 Map
的 ThreadLocal.ThreadLocalMap
數據結構來存儲以 ThreadLocal
類型的變量爲 Key
的數值,並用 ThreadLocal
來存取刪,操作 ThreadLocalMap
。
-
當我們定義一個
ThreadLocal
變量時,其實就是在定義一個Key
-
當我們調用
set(v)
方法時,就是以當前ThreadLocal
變量爲key
,傳入參數爲value
,向ThreadLocal.ThreadLocalMap
存數據 -
當我們調用
get()
方法時,就是以當前ThreadLocal
變量爲key
,從ThreadLocal.ThreadLocalMap
取對應的數據
擴 展
一、ThreadLocalMap的Hash衝突解決辦法
採用線性探測的方式,根據 key
計算 hash
值,如果出現衝突,則向後探測,當到哈希表末尾的時候再從0開始,直到找到一個合適的位置。
這種算法也決定了 ThreadLocalMap
不適合存儲大量數據。
二、ThreadLocalMap的擴容問題
ThreadLocalMap
初始大小爲 16
,加載因子爲 2/3
,當 size
大於 threshold
時,就會進行擴容。
擴容時,新建一個大小爲原來數組長度的兩倍的數組,然後遍歷舊數組中的 entry
並將其插入到新的hash數組中,在擴容的時候,會把 key
爲 null
的 Entry
的 value
值設置爲 null
,以便內存回收,減少內存泄漏問題。