Java 200+ 面试题补充 ThreadLocal 模块
讓我們每天都有進(jìn)步,老王帶你打造最全的 Java 面試清單,認(rèn)真把一件事做到極致。
本文是前文《Java 最常見的 200+ 面試題》的第一個(gè)補(bǔ)充模塊。
1.ThreadLocal 是什么?
ThreadLocal 是一個(gè)本地線程副本變量工具類。主要用于將私有線程和該線程存放的副本對(duì)象做一個(gè)映射,各個(gè)線程之間的變量互不干擾,在高并發(fā)場(chǎng)景下,可以實(shí)現(xiàn)無狀態(tài)的調(diào)用,適用于各個(gè)線程不共享變量值的操作。
2.ThreadLocal 工作原理是什么?
ThreadLocal 原理:每個(gè)線程的內(nèi)部都維護(hù)了一個(gè) ThreadLocalMap,它是一個(gè) Map(key,value)數(shù)據(jù)格式,key 是一個(gè)弱引用,也就是 ThreadLocal 本身,而 value 存的是線程變量的值。
也就是說 ThreadLocal 本身并不存儲(chǔ)線程的變量值,它只是一個(gè)工具,用來維護(hù)線程內(nèi)部的 Map,幫助存和取變量。
數(shù)據(jù)結(jié)構(gòu),如下圖所示:
(圖片來源于網(wǎng)絡(luò))
3.ThreadLocal 如何解決 Hash 沖突?
與 HashMap 不同,ThreadLocalMap 結(jié)構(gòu)非常簡(jiǎn)單,沒有 next 引用,也就是說 ThreadLocalMap 中解決 Hash 沖突的方式并非鏈表的方式,而是采用線性探測(cè)的方式。所謂線性探測(cè),就是根據(jù)初始 key 的 hashcode 值確定元素在 table 數(shù)組中的位置,如果發(fā)現(xiàn)這個(gè)位置上已經(jīng)被其他的 key 值占用,則利用固定的算法尋找一定步長的下個(gè)位置,依次判斷,直至找到能夠存放的位置。
源代碼實(shí)現(xiàn)如下:
/* Increment i modulo len.*/ private static int nextIndex(int i, int len) {return ((i + 1 < len) ? i + 1 : 0); }/* Decrement i modulo len.*/ private static int prevIndex(int i, int len) {return ((i - 1 >= 0) ? i - 1 : len - 1); }4.ThreadLocal 的內(nèi)存泄露是怎么回事?
ThreadLocal 在 ThreadLocalMap 中是以一個(gè)弱引用身份被 Entry 中的 Key 引用的,因此如果 ThreadLocal 沒有外部強(qiáng)引用來引用它,那么 ThreadLocal 會(huì)在下次 JVM 垃圾收集時(shí)被回收。這個(gè)時(shí)候 Entry 中的 key 已經(jīng)被回收,但是 value 又是一強(qiáng)引用不會(huì)被垃圾收集器回收,這樣 ThreadLocal 的線程如果一直持續(xù)運(yùn)行,value 就一直得不到回收,這樣就會(huì)發(fā)生內(nèi)存泄露。
5.為什么 ThreadLocalMap 的 key 是弱引用?
我們知道 ThreadLocalMap 中的 key 是弱引用,而 value 是強(qiáng)引用才會(huì)導(dǎo)致內(nèi)存泄露的問題,至于為什么要這樣設(shè)計(jì),這樣分為兩種情況來討論:
- key 使用強(qiáng)引用:這樣會(huì)導(dǎo)致一個(gè)問題,引用的 ThreadLocal 的對(duì)象被回收了,但是 ThreadLocalMap 還持有 ThreadLocal 的強(qiáng)引用,如果沒有手動(dòng)刪除,ThreadLocal 不會(huì)被回收,則會(huì)導(dǎo)致內(nèi)存泄漏。
- key 使用弱引用:這樣的話,引用的 ThreadLocal 的對(duì)象被回收了,由于 ThreadLocalMap 持有 ThreadLocal 的弱引用,即使沒有手動(dòng)刪除,ThreadLocal 也會(huì)被回收。value 在下一次 ThreadLocalMap 調(diào)用 set、get、remove 的時(shí)候會(huì)被清除。
比較以上兩種情況,我們可以發(fā)現(xiàn):由于 ThreadLocalMap 的生命周期跟 Thread 一樣長,如果都沒有手動(dòng)刪除對(duì)應(yīng) key,都會(huì)導(dǎo)致內(nèi)存泄漏,但是使用弱引用可以多一層保障,弱引用 ThreadLocal 不會(huì)內(nèi)存泄漏,對(duì)應(yīng)的 value 在下一次 ThreadLocalMap 調(diào)用 set、get、remove 的時(shí)候被清除,算是最優(yōu)的解決方案。
6.ThreadLocal 的應(yīng)用場(chǎng)景有哪些?
ThreadLocal 適用于獨(dú)立變量副本的情況,比如 Hibernate 的 session 獲取場(chǎng)景。
示例代碼:
private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();public static Session getCurrentSession(){Session session = threadLocal.get();try {if(session ==null&&!session.isOpen()){//...}threadLocal.set(session);} catch (Exception e) {// TODO: handle exception}return session; }查看所有面試題:《Java 最常見 200+ 面試題》
參考資料
https://www.jianshu.com/p/a1cd61fa22da
https://www.jianshu.com/p/98b68c97df9b
掃描下方二維碼,關(guān)注更多動(dòng)態(tài):
往期文章推薦:
《Java 最常見的 200+ 面試題》
《你真的懂 == 和 equals 的區(qū)別嗎?》
《程序員精美簡(jiǎn)歷Top榜—面試必備》
《程序員專屬精美簡(jiǎn)歷合集——第二彈》
總結(jié)
以上是生活随笔為你收集整理的Java 200+ 面试题补充 ThreadLocal 模块的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 优秀的基数统计算法——HyperLogL
- 下一篇: 多图证明,Java到底是值传递还是引用传