深度解析ThreadLocal原理

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"今天呢,和大家聊一下"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1. 是什麼?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"JDK1.2"}]},{"type":"text","text":"提供的的一個線程綁定變量的類。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"他的思想就是:給每一個使用到這個資源的線程都克隆一份,實現了不同線程使用不同的資源,且該資源之間相互獨立"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2. 爲什麼用?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"思考一個場景"},{"type":"text","text":":數據庫連接的時候,我們會創建一個"},{"type":"codeinline","content":[{"type":"text","text":"Connection"}]},{"type":"text","text":"連接,讓不同的線程使用。這個時候就會出現多個線程爭搶同一個資源的情況。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這種多個線程爭搶同一個資源的情況,很常見,我們常用的解決辦法也就兩種:"},{"type":"text","marks":[{"type":"strong"}],"text":"空間換時間,時間換空間"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"沒有辦法,魚與熊掌不可兼得也。就如我們的"},{"type":"codeinline","content":[{"type":"text","text":"CAP"}]},{"type":"text","text":"理論,也是犧牲其中一項,保證其他兩項。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而針對上面的場景我們的解決辦法如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"空間換時間:爲每一個線程創建一個連接。直接在線程工作中,創建一個連接。("},{"type":"text","marks":[{"type":"strong"}],"text":"重複代碼太多"},{"type":"text","text":")使用"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}]},{"type":"text","text":",爲每一個線程綁定一個連接。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"時間換空間:對當前資源加鎖,每一次僅僅存在一個線程可以使用這個連接。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}]},{"type":"text","text":"爲每一個線程綁定一個指定類型的變量,相當於線程私有化"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"3. 怎麼用?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"ThreadLocal threadLocal = new ThreadLocal<>();\nthreadLocal.get();\nthreadLocal.set(1);\nthreadLocal.remove();\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"沒錯,這四行代碼已經把"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}]},{"type":"text","text":"的使用方法表現得明明白白。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"get"}]},{"type":"text","text":"從"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}]},{"type":"text","text":"拿出一個當前線程所擁有得對象"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"set"}]},{"type":"text","text":"給當前線程綁定一個對象"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"remove"}]},{"type":"text","text":"將當前線程綁定的當前對象移除"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"記住在使用的以後,一定要remove,一定要remove,一定要remove"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲什麼要"},{"type":"codeinline","content":[{"type":"text","text":"remove"}]},{"type":"text","text":"。相信不少小夥伴聽到過"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}]},{"type":"text","text":"會導致內存泄漏問題。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"沒錯,所以爲了解決這種情況,"},{"type":"text","marks":[{"type":"strong"}],"text":"所以你懂吧,用完就移除,別浪費空間(渣男欣慰)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看到這,腦袋上有好多問號出現了("},{"type":"text","marks":[{"type":"strong"}],"text":"小朋友你是否有很多問號?"},{"type":"text","text":")"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"爲啥會引發內存泄漏?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"爲啥不remove就內存泄漏了"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"它是怎麼講對象和線程綁定的"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"爲啥get的時候拿到的就是當前線程的而不是其他線程的"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"它怎麼實現的???"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"來吧,"},{"type":"text","marks":[{"type":"strong"}],"text":"開淦,源碼來"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"4. 源碼解讀"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先來說一個思路:"},{"type":"text","marks":[{"type":"strong"}],"text":"如果我們自己寫一個"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"會咋寫?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"線程綁定一個對象。**這難道不是我們熟知的"},{"type":"codeinline","content":[{"type":"text","text":"map"}]},{"type":"text","text":"映射?**有了"},{"type":"codeinline","content":[{"type":"text","text":"Map"}]},{"type":"text","text":"我們就可以以線程爲"},{"type":"codeinline","content":[{"type":"text","text":"Key"}]},{"type":"text","text":",對象爲"},{"type":"codeinline","content":[{"type":"text","text":"value"}]},{"type":"text","text":"添加到一個集合中,然後各種"},{"type":"codeinline","content":[{"type":"text","text":"get,set,remove"}]},{"type":"text","text":"操作,想怎麼玩就怎麼玩,搞定。"},{"type":"text","marks":[{"type":"strong"}],"text":"😀"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"這個時候,有兄弟說了。你這思路不對啊,你這一個線程僅僅只能存放一個類型的變量,那我想存多個呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"摸摸自己充盈的髮量,你說出了一句至理名言:"},{"type":"text","marks":[{"type":"strong"}],"text":"萬般問題,皆繫於源頭和結果之中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從結果考慮,讓開發者自己搞線程私有(估計被會開發者罵死)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"來吧,從源頭考慮。現在我們的需求是:"},{"type":"text","marks":[{"type":"strong"}],"text":"線程可以綁定多個值,而不僅僅是一個"},{"type":"text","text":"。嗯,沒錯,兄弟們把你們的想法說出來。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"讓線程自己維護一個Map,將這個"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"作爲"},{"type":"codeinline","content":[{"type":"text","text":"Key"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":",對象作爲"},{"type":"codeinline","content":[{"type":"text","text":"Value"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"不就搞定了"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"兄弟,牛掰旮旯四"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule"},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此時,又有兄弟說了。按照你這樣的做法,將"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}]},{"type":"text","text":"扔到線程本身的的Map裏,那豈不是這個"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}]},{"type":"text","marks":[{"type":"strong"}],"text":"一直被線程對象引用,所以在線程銷燬之前都是可達的,都無法"},{"type":"codeinline","content":[{"type":"text","text":"GC"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"呀,有"},{"type":"codeinline","content":[{"type":"text","text":"BUG"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"啊"},{"type":"text","text":"???"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"**好,問題。**這樣想,既然由於線程和"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}]},{"type":"text","text":"對象存在引用,導致無法"},{"type":"codeinline","content":[{"type":"text","text":"GC"}]},{"type":"text","text":",那我將你和線程之間的引用搞成弱引用或者軟引用不就成了。一"},{"type":"codeinline","content":[{"type":"text","text":"GC"}]},{"type":"text","text":"你就沒了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"啥,你不知道啥是弱引用和軟引用???"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前面講過的東西,算啦再給你們複習一波。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"JDK"}]},{"type":"text","text":"中存在四種類型引用,默認是強引用,也就是我們經常乾的事情。瘋狂"},{"type":"codeinline","content":[{"type":"text","text":"new,new,new"}]},{"type":"text","text":"。這個時候創建的對象都是強引用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"強引用。直接"},{"type":"codeinline","content":[{"type":"text","text":"new"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"軟引用。通過"},{"type":"codeinline","content":[{"type":"text","text":"SoftReference"}]},{"type":"text","text":"創建,在內存空間不足的時候直接銷燬,即它可能最後的銷燬地點是在老年區"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"弱引用。通過"},{"type":"codeinline","content":[{"type":"text","text":"WeakReference"}]},{"type":"text","text":"創建,在"},{"type":"codeinline","content":[{"type":"text","text":"GC"}]},{"type":"text","text":"的時候直接銷燬。即其銷燬地點必定爲伊甸區"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"虛引用。通過"},{"type":"codeinline","content":[{"type":"text","text":"PhantomReference"}]},{"type":"text","text":"創建,它和不存也一樣,"},{"type":"text","marks":[{"type":"strong"}],"text":"非常虛,只能通過引用隊列在進行一些操作,主要用於堆外內存回收"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"好了,回到正題,上面的引用裏最適合我們當前的場景的就是弱引用了,"},{"type":"text","marks":[{"type":"strong"}],"text":"爲什麼這個樣子說:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在以往我們使用完對象以後等着"},{"type":"codeinline","content":[{"type":"text","text":"GC"}]},{"type":"text","text":"清理,但是對於"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}]},{"type":"text","text":"來說,即使我們使用結束,也會因爲線程本身存在該對象的引用,處於對象可達狀態,垃圾回收器無法回收。這個時候當"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}]},{"type":"text","text":"太多的時候就會出現內存泄漏的問題。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而我們將"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}]},{"type":"text","text":"對象的引用作爲弱引用,那麼就很好的解決了這個問題。當我們自己使用完"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}]},{"type":"text","text":"以後,"},{"type":"text","marks":[{"type":"strong"}],"text":"當"},{"type":"codeinline","content":[{"type":"text","text":"GC"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"的時候就會將我們創建的強引用直接幹掉,而這個時候我們完全可以將線程"},{"type":"codeinline","content":[{"type":"text","text":"Map"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"中的引用幹掉,於是使用了弱引用,這個時候大家應該懂了爲啥不使用軟引用了吧"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"還有一個問題:爲什麼會引發內存泄漏呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"瞭解"},{"type":"codeinline","content":[{"type":"text","text":"Map"}]},{"type":"text","text":"結構的兄弟們應該清楚,內部實際就一個節點數組,對於"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocalMap"}]},{"type":"text","text":"而言,內部是一個"},{"type":"codeinline","content":[{"type":"text","text":"Entity"}]},{"type":"text","text":",它將"},{"type":"codeinline","content":[{"type":"text","text":"Key"}]},{"type":"text","text":"作爲弱引用,"},{"type":"codeinline","content":[{"type":"text","text":"Value"}]},{"type":"text","text":"還是強引用。如果我們在使用完"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}]},{"type":"text","text":"以後,沒有對"},{"type":"codeinline","content":[{"type":"text","text":"Entity"}]},{"type":"text","text":"進行移除,會引發內存泄漏問題。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"ThreadLocalMap"}]},{"type":"text","text":"提供了一個方法"},{"type":"codeinline","content":[{"type":"text","text":"expungeStaleEntry"}]},{"type":"text","text":"方法用來排除無效的"},{"type":"codeinline","content":[{"type":"text","text":"Entity"}]},{"type":"text","text":"("},{"type":"codeinline","content":[{"type":"text","text":"Key"}]},{"type":"text","text":"爲空的實體)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"說到這裏,有一個問題我思考了蠻久的,value爲啥不搞成弱引用,用完直接扔了多好"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後思考出來得答案(按照源碼推了一下):"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"不設置爲弱引用,是因爲不清楚這個"},{"type":"codeinline","content":[{"type":"text","text":"Value"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"除了"},{"type":"codeinline","content":[{"type":"text","text":"map"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"的引用還是否還存在其他引用,如果不存在其他引用,當"},{"type":"codeinline","content":[{"type":"text","text":"GC"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"的時候就會直接將這個Value幹掉了,而此時我們的"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"還處於使用期間,就會造成Value爲null的錯誤,所以將其設置爲強引用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而爲了解決這個強引用的問題,它提供了一種機制就是上面我們說的將"},{"type":"codeinline","content":[{"type":"text","text":"Key"}]},{"type":"text","text":"爲"},{"type":"codeinline","content":[{"type":"text","text":"Null"}]},{"type":"text","text":"的"},{"type":"codeinline","content":[{"type":"text","text":"Entity"}]},{"type":"text","text":"直接清除"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"到這裏,這個類的設計已經很清楚了。接下來我們看一下源碼吧!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule"},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"需要注意的一個點是:"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocalMap"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"解決哈希衝突的方式是線性探測法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"人話就是:如果當前數組位有值,則判斷下一個數組位是否有值,如果有值繼續向下尋找,直到一個爲空的數組位"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Set方法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"class ThreadLocal\t\n\tpublic void set(T value) {\n \t//拿到當前線程\n Thread t = Thread.currentThread();\n //獲取當前線程的ThreadLocalMap\n ThreadLocalMap map = getMap(t);\n if (map != null)\n //如果當前線程的Map已經創建,直接set\n map.set(this, value);\n else\n //沒有創建,則創建Map\n createMap(t, value);\n }\n\n\tprivate void set(ThreadLocal> key, Object value) {\n Entry[] tab = table;\n int len = tab.length;\n int i = key.threadLocalHashCode & (len-1);\n\t\t\t//拿到當前數組位,當前數組位是否位null,如果爲null,直接賦值,如果不爲null,則線性查找一個null,賦值\n for (Entry e = tab[i];\n e != null;\n e = tab[i = nextIndex(i, len)]) {\n ThreadLocal> k = e.get();\n\n if (k == key) {\n e.value = value;\n return;\n }\n\n if (k == null) {\n replaceStaleEntry(key, value, i);\n return;\n }\n }\n\n tab[i] = new Entry(key, value);\n int sz = ++size;\n //清除一些失效的Entity\n if (!cleanSomeSlots(i, sz) && sz >= threshold)\n rehash();\n }\n\n\n\tThreadLocalMap getMap(Thread t) {\n //獲取當前線程的ThreadLocalMap\n return t.threadLocals;\n }\n\n\tvoid createMap(Thread t, T firstValue) {\n \t//當前對象作爲Key,和我們的設想一樣\n t.threadLocals = new ThreadLocalMap(this, firstValue);\n }\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Get方法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"\tpublic T get() {\n //獲取當前線程\n Thread t = Thread.currentThread();\n //拿到當前線程的Map\n ThreadLocalMap map = getMap(t);\n if (map != null) {\n //獲取這個實體\n ThreadLocalMap.Entry e = map.getEntry(this);\n if (e != null) {\n @SuppressWarnings(\"unchecked\")\n T result = (T)e.value;\n //返回\n return result;\n }\n }\n return setInitialValue();\n }\n\n\tprivate Entry getEntry(ThreadLocal> key) {\n //計算數組位\n int i = key.threadLocalHashCode & (table.length - 1);\n Entry e = table[i];\n //如果當前數組有值,且數組位的key相同,則返回value\n if (e != null && e.get() == key)\n return e;\n else\n //線性探測尋找對應的Key\n return getEntryAfterMiss(key, i, e);\n }\n\n\tprivate Entry getEntryAfterMiss(ThreadLocal> key, int i, Entry e) {\n Entry[] tab = table;\n int len = tab.length;\n\n while (e != null) {\n ThreadLocal> k = e.get();\n if (k == key)\n return e;\n if (k == null)\n //排除當前爲空的Entity\n expungeStaleEntry(i);\n else\n //獲取下一個數組位\n i = nextIndex(i, len);\n e = tab[i];\n }\n //如果沒有找到直接返回空\n return null;\n }\n\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"remove"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"\tpublic void remove() {\n ThreadLocalMap m = getMap(Thread.currentThread());\n if (m != null)\n m.remove(this);\n }\n\n\tprivate void remove(ThreadLocal> key) {\n Entry[] tab = table;\n int len = tab.length;\n int i = key.threadLocalHashCode & (len-1);\n //拿到當前的數組,判斷是否爲需要的數組位,如果不是線性查找\n for (Entry e = tab[i];\n e != null;\n e = tab[i = nextIndex(i, len)]) {\n if (e.get() == key) {\n e.clear();\n //清空位NUll的實體\n expungeStaleEntry(i);\n return;\n }\n }\n }\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"我們可以看到一個現象:在"},{"type":"codeinline","content":[{"type":"text","text":"set"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":","},{"type":"codeinline","content":[{"type":"text","text":"get"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":","},{"type":"codeinline","content":[{"type":"text","text":"remove"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"的時候都調用了"},{"type":"codeinline","content":[{"type":"text","text":"expungeStaleEntry"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"來將所有失效的"},{"type":"codeinline","content":[{"type":"text","text":"Entity"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"移除"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看一下這個方法做了什麼"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"private int expungeStaleEntry(int staleSlot) {\n Entry[] tab = table;\n int len = tab.length;\n\n // 刪除實體的Value\n tab[staleSlot].value = null;\n //置空這個數組位\n tab[staleSlot] = null;\n //數量減一\n size--;\n\n // 重新計算一次哈希,如果當前數組位不爲null,線性查找直到一個null\n Entry e;\n int i;\n for (i = nextIndex(staleSlot, len);\n (e = tab[i]) != null;\n i = nextIndex(i, len)) {\n ThreadLocal> k = e.get();\n if (k == null) {\n e.value = null;\n tab[i] = null;\n size--;\n } else {\n int h = k.threadLocalHashCode & (len - 1);\n if (h != i) {\n tab[i] = null;\n\n // Unlike Knuth 6.4 Algorithm R, we must scan until\n // null because multiple entries could have been stale.\n while (tab[h] != null)\n h = nextIndex(h, len);\n tab[h] = e;\n }\n }\n }\n return i;\n }"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"看完三件事❤️"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你覺得這篇內容對你還蠻有幫助,我想邀請你幫我三個小忙:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":""}]},{"type":"numberedlist","attrs":{"start":null,"normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"點贊,轉發,有你們的 『點贊和評論』,纔是我創造的動力。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"關注公衆號 『 "},{"type":"text","marks":[{"type":"strong"}],"text":"java爛豬皮"},{"type":"text","text":" 』,不定期分享原創知識。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"同時可以期待後續文章ing🚀"}]}]}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/34/34172ad7f3cc8e0f28bd1fc6ca2d2b68.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原文出處:https://my.oschina.net/onlyzuo/blog/4704594"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章