面試 ThreadLocal,被問懵了?看完這篇文章你就穩(wěn)了!
引言
小米最近在做社招面試,遇到了一位候選人,面試到 Java 并發(fā)時(shí),聊到了 ThreadLocal,候選人一開始信心滿滿:“這個(gè)我會(huì)!” 結(jié)果,深挖幾輪之后,發(fā)現(xiàn)這位候選人只會(huì)皮毛,甚至踩了不少坑……
于是,今天就和大家聊聊 ThreadLocal 這個(gè)面試高頻考點(diǎn),不僅讓你能答出基礎(chǔ)概念,還能講出實(shí)際使用場景,讓面試官對你刮目相看!
什么是 ThreadLocal?
想象一下,你去健身房辦了一張私教卡,每次去健身房,教練都會(huì)給你專屬定制的訓(xùn)練計(jì)劃,而不會(huì)讓你去練別人的計(jì)劃。
ThreadLocal 就像這張 私教卡,它的核心作用就是:讓每個(gè)線程都能擁有自己的專屬變量,而不會(huì)影響到其他線程。
在 Java 代碼中,我們通常用 ThreadLocal 來 存儲(chǔ)每個(gè)線程獨(dú)有的數(shù)據(jù),避免線程之間的數(shù)據(jù)污染。來看一個(gè)最簡單的例子:
圖片
運(yùn)行結(jié)果:
圖片
每個(gè)線程都有自己獨(dú)立的 ThreadLocal 變量,即使修改了變量的值,也不會(huì)影響其他線程!
ThreadLocal 的工作原理
1. 底層數(shù)據(jù)結(jié)構(gòu)
ThreadLocal 的底層實(shí)現(xiàn)其實(shí)是 每個(gè)線程內(nèi)部維護(hù)一個(gè) ThreadLocalMap,這個(gè) Map 以 ThreadLocal 變量為 key,具體的值為 value。
簡單來說,每個(gè)線程內(nèi)部都有一個(gè)類似這樣的數(shù)據(jù)結(jié)構(gòu):
圖片
當(dāng)我們調(diào)用 threadLocal.set(value) 時(shí),數(shù)據(jù)并不會(huì)存儲(chǔ)到 ThreadLocal 對象本身,而是存放在 當(dāng)前線程的 ThreadLocalMap 里。
2. 內(nèi)存泄漏問題
ThreadLocal 設(shè)計(jì)上是弱引用,但 ThreadLocalMap 里的 value 是強(qiáng)引用,如果不手動(dòng)清理 ThreadLocal.remove(),可能會(huì)導(dǎo)致內(nèi)存泄漏。來看一個(gè)坑:
圖片
如果線程池中線程復(fù)用,ThreadLocal 沒有 remove(),線程的 ThreadLocalMap 可能無法被回收,從而導(dǎo)致內(nèi)存泄漏。
最佳實(shí)踐: 每次使用完 ThreadLocal,記得調(diào)用 remove(),防止內(nèi)存泄漏!
ThreadLocal 典型使用場景
1. 用戶身份信息存儲(chǔ)(常見)
在 Web 應(yīng)用中,每個(gè)請求通常都有自己的 用戶身份信息,比如 登錄用戶 ID。我們可以用 ThreadLocal 來存儲(chǔ)用戶信息,保證在同一個(gè)線程的多個(gè)方法調(diào)用中,都能訪問到當(dāng)前用戶的信息。
圖片
使用方式:
圖片
因?yàn)?HTTP 請求是多線程并發(fā)的,如果使用全局變量存儲(chǔ) userId,會(huì)導(dǎo)致數(shù)據(jù)污染!但用 ThreadLocal,每個(gè)請求的 userId 只存儲(chǔ)在自己的線程中,互不影響。
2. 事務(wù)管理(數(shù)據(jù)庫連接)
在 Spring 的事務(wù)管理中,ThreadLocal 被用來存儲(chǔ) 數(shù)據(jù)庫連接,保證同一個(gè)事務(wù)中使用同一個(gè)數(shù)據(jù)庫連接。
3. 日志跟蹤(Tracing)
在分布式系統(tǒng)中,我們經(jīng)常需要給每個(gè)請求分配一個(gè)唯一的追蹤 ID(Trace ID),用來跟蹤整個(gè)請求的執(zhí)行流程。ThreadLocal 也是一個(gè)很好的選擇:
圖片
然后在日志中加上 Trace ID:
圖片
這樣,整個(gè)請求在不同的日志中都有相同的 Trace ID,方便排查問題!
ThreadLocal 的優(yōu)缺點(diǎn)
總結(jié)
ThreadLocal 是 Java 并發(fā)中的 “線程局部變量”,常用于存儲(chǔ)線程獨(dú)有的數(shù)據(jù),避免線程間的數(shù)據(jù)污染。它的常見使用場景包括:
- 用戶身份信息存儲(chǔ)
- 事務(wù)管理(數(shù)據(jù)庫連接)
- 日志追蹤(Tracing)
但使用時(shí)一定要注意內(nèi)存泄漏問題,記得在適當(dāng)?shù)臅r(shí)機(jī)調(diào)用 remove() 方法。