從源碼級別揭開ThreadLocal的神祕面紗

 說在前面 

ThreadLocal用來提供線程級別變量,變量只對當前線程可見。相比與“使用鎖控制共享變量訪問順序”的解決方案。ThreadLocal通過空間換時間的方案,規避了競爭問題,因爲每個線程都有屬於自己的變量。

此時就產生了第一個問題:線程如何維護屬於自己的變量副本,搞懂了這個也就搞懂了其原理。

  源碼分析 

一、首先看下Thread類

Thread中有一個 threadLocals 屬性表示線程的本地變量。這個屬性的類型是 ThreadLocal.ThreadLocalMap

ThreadLocalMapThreadLocal的內部類,他是類 Map結構,也是存儲 K-V結構數據,並用 Entry封裝 K-V。不同的是 ThreadLocalMapEntryKey只能是 ThreadLocal類型對象,並且是一個弱引用。

 也就是說線程通過一個類Map數據結構 ThreadLocal.ThreadLocalMap 來存儲屬於自己的線程變量。

三、ThreadLocal本尊

ThreadLocalMap賦值、取值操作的入口在其外部類 ThreadLocal中。

set(v)方法內調用 ThreadLocalMapset(this,v)方法存值。(類似 Mapput(k,v)方法)

 get()方法內調用 ThreadLocalMapgetEntry(this)方法取值(類似 Mapget(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用類似 MapThreadLocal.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數組中,在擴容的時候,會把 keynullEntryvalue值設置爲 null,以便內存回收,減少內存泄漏問題。

 

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