sqlserver2008未将对象引用设置到对象的实例_面试官:ThreadLocal 的内存泄漏是弱引用导致的,你确定?...
面試官:ThreadLocal 了解嗎?
Python 小星:線程局部變量,多線程下能保證各個線程的變量相對獨立于其他線程的變量。
面試官:那你說下它是如何保證線程隔離的?
Python 小星:每個線程維護一個 ThreadLocalMap ,ThreadLocalMap 和 HashMap 結構類似,key 是 ThreadLocal,通過 hash 算法取余平均分配到數組上,value 是我們需要的局部變量。有與 key 的唯一性,保證了線程隔離。
面試官:那你說說 ThreadLocalMap 是數組加鏈表的結構解決沖突的嗎?
Python 小星:我的印象里不是鏈表結構。
面試官:那他是如何解決沖突的?
Python 小星:......
面試官:創建多線程異步執行業務邏輯時,該 ThreadLocal 變量并不能傳遞到子線程中,如何讓父子線程共享變量?
Python 小星:不太清楚
面試官:InheritableThreadLocal 可以下去了解下。今天就先到這,回去等通知吧
什么是 ThreadLocal?(以下簡稱 TL)
看下JDK源碼注釋
翻譯過來:
TL 用來提供線程內部局部變量。這種變量在多線程環境下訪問(通過 get 和 set 方法訪問)時能保證各個線程的變量相對獨立于其他線程的變量。TL 實例通常來說都是 private static 類型,用于關聯線程和線程上下文。
簡單說,ThreadLocal 為每個使用該變量的線程提供獨立的變量副本。所以每個線程都可以獨立地改變自己的副本,而不會影響其他線程所對應的副本,我們通常把這叫“線程隔離”。
ThreadLocal 的基本使用
1、常用方法
2、如果不是 TL,會出現什么問題???
輸出結果(多運行幾次):
我們可以發現,線程 0 輸出了線程 2 的數據。
3、使用 TL
TL 如何實現線程隔離???
1、JDK 早期設計
熟悉 hashmap 的老鐵們,比較容易理解:每個 TL 創建一個 Map,然后用線程作為 Map 的 key,要存儲的局部變量作為 Map 的 value,這樣達到線程隔離的目的。
這么做有一個弊端:
當線程回收時,該線程綁定的變量不能被自動的回收,因為變量存儲在 ThreadLocal 里,必須顯式的去回收。
2、JDK 1.8 的設計
每個 Thread 維護一個 ThreadLocalMap,這 map 的 key 是 ThreadLocal 實例本身,value 才是真正要存儲的變量值。
① 每個 Thread 內部都有一個 ThreadLocalMap
② ThreadLocalMap 里面存儲 TL 對象(key) 和 線程的變量副本(value)
③ Thread 內部的 ThreadLocalMap 是由 TL 維護的,由 TL 負責向 ThreadLocalMap 設置和獲取變量值
④ 對于不同的線程,每次獲取副本值時,別的線程并不能獲取當前線程的副本值,形成副本的隔離,互不干擾
好處:
① 每個 ThreadLocalMap 存儲的 Entry 數量變少
② 當 Thread 銷毀時,ThreadLocalMap 也隨之銷毀,減少內存的使用
TL 源碼
1、set 方法
① 首先獲取當前線程,并根據當前線程獲取一個 map
② 如果獲取的 map 不為空,則將參數設置到 map 中(當前的 TL 的引用設置為 key)
③ 如果 map 為空,則給該線程創建 map ,并設置初始值
2、get 方法
初始化
① 獲取當前線程,然后根據當前線程獲取 map
② 如果 map 不為空,則以 TL 的引用為 key 獲取 map 中對應的 Entry e,否則到第 ④ 步
③ 如果 e 不為空,獲取對應的 value 值,否則到第 ④ 步
④ map 為空 或者 e 為空,通過 initialValue ,也就是 NULL,然后用 TL 的引用和 value 作為 firstKey 和 firstValue 創建新的 map
總結:獲取當前線程的 ThreadLocalMap,如果存在則返回值,不存在則創建并返回值。
3、remove
4、ThreadLocalMap
ThreadLocalMap 是 TL 的內部類,沒有實現 map 接口。
成員變量
從圖中,我們可以發現 SHI 和 HashMap 類似。
Entry
Entry 繼承 WeakReference,也就是說 key 是弱引用。
弱引用和內存泄漏
1、弱引用相關概念
① java 引用
4 種類型:強、弱、軟、虛
② 強引用和弱引用
【強引用】:最常見的 new 一個對象,只要強引用指向一個對象,GC 就不會回收
【弱引用】:GC 一旦發現弱引用的對象,不管當前內存空間足夠與否,都會回收它的內存
2、內存溢出和內存泄漏
① 內存溢出(memory overflow)
沒有足夠的內存提供申請者使用。就像一個開水瓶,當你倒水時,如果大于水瓶容量,水就會溢出來。
② 內存泄漏(memory leak)
動態分配的堆內存由于某種原因未釋放或者無法釋放,造成系統內存的浪費,內存泄漏的堆積將造成內存泄漏。
為什么 TL 里使用弱引用?
首先我們明確 key 是使用了弱引用,當把 threadlocal 實例置為 null 以后,沒有任何強引用指向 threadlocal 實例,所以 threadlocal 就可以順利被 gc 。
我們從圖中也能看到雖然 key 被 gc 回收了,但是 value 還在呀。因為存在一條從 current thread 連接過來的強引用。只有當前 thread 結束以后,current thread 就不會存在棧中,強引用斷開,Current Thread, Map, value 將全部被 GC 回收。
有人會問:無論是強引用還是弱引用,都會出現內存泄漏?用弱引用的好處到底體現在哪?
弱引用會將 key 設置為 null,當使用 map 的 get 和 set 方法時,會判斷 key 是否為 null ,如果為 null,則將 value 設置為 null。弱引用比強引用在 thread 結束之前多一層屏障。
如何解決內存泄漏問題
當線程的某個 localThread 使用完,然后調用 threadlocal 的 remove 方法。
ThreadLocalMap 和 HashMap 的區別?
ThreadLocalMap 和 HashMap 最大的區別在于解決 hash 沖突。
HashMap 使用的拉鏈法,而 ThreadLocalMap 使用的線性探測法。
簡單說:
當我們通過 int i = key.threadLocalHashCode & (len-1) 計算出 hash 值,如果出現沖突,順序查看表中下一單元,直到找出一個空單元或查遍全表。
正因為采取的線性探測法解決沖突,所以在查找的時候,必須比較 key 值是否相等,否則順序尋找下一個單元。
父子線程如何共享變量?
使用 InheritableThreadLocal 來解決,底層原理的話各位看官下去看看,下次再續。
輸出結果:
@Python大星 | 文
總結
以上是生活随笔為你收集整理的sqlserver2008未将对象引用设置到对象的实例_面试官:ThreadLocal 的内存泄漏是弱引用导致的,你确定?...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python中自定义函数如何传递动态参数
- 下一篇: phpstrom查看代码总行数_歪特内推