為什么 ThreadLocal 可以做到線程隔離?
先通過一個小例子感受一下:
運行結(jié)果:
OK,從效果上看,ThreadLocal 確實是線程隔離的,那么,它是如何做到線程隔離的呢?下面我們扒一扒源碼,看看它是如何做到的:
set() 方法的邏輯如下:
獲取當前線程
根據(jù)當前線程獲取一個 ThreadLocalMap 對象
如果 map 不為 null 則保存
如果 map 為 null 則創(chuàng)建一個 map
getMap() 和 createMap() 方法都干了啥呢?我們點進去看:
進入到兩個方法內(nèi)部后發(fā)現(xiàn),不管執(zhí)行哪個分支,最終是把值保存到了當前線程的 threadLocals 屬性中。
查看 Thread 類的源碼,你會發(fā)現(xiàn)類中定義了一個 threadLocals 屬性,且初始值為 null,其類型為 ThreadLocal.ThreadLocalMap。
到此,我們發(fā)現(xiàn)了,原來 ThreadLocal 就是把我們要傳遞的對象放到了當前線程的 threadLocals 屬性中。也就是說每個線程在用 ThreadLocal 保存對象時,其實就是將對象放到了當前線程實例對象的 threadLocals 屬性里面。這樣一來線程之間自然就是互相獨立的啦。
再看看 get() 方法:
ThreadLocal 的 get() 方法其實和 set() 方法邏輯很相似,先從當前線程的 threadLocals 屬性中取,如果該屬性為 null,那么就初始化。
當線程結(jié)束時,會調(diào)用當前線程實例的 exit() 方法,將 threadLocals 設(shè)置為 null,以便垃圾回收器將其回收掉。
最后,有一點需要格外注意:用完 ThreadLocal 一定要記得手動調(diào)用 remove() 方法,否則可能會產(chǎn)生臟數(shù)據(jù)甚至產(chǎn)生內(nèi)存泄漏。
為啥呢?上面不是說線程結(jié)束時,會將 threadLocals 置為 null 嗎?
是的,線程結(jié)束時,確實會做清理工作。
但,如果線程一直不結(jié)束呢?如果線程會被復用呢?比如使用了線程池。
所以,使用 ThreadLocal 一定要手動 remove()。
更多獨家精彩內(nèi)容盡在我的新書《Spring Boot趣味實戰(zhàn)課》中。?