ThreadLocal是什么
ThreadLocal提供了線程的局部變量品山,每個線程訪問獨立的變量副本,實現了線程的數據隔離烤低。
ThreadLocal實現原理
每個線程Thread實例中包含兩個ThreadLocalMap實例:threadLocals
和inheritableThreadLocals
肘交,讀取ThreadLocal實例時,以ThreadLocal實例為key從當前線程的threadLocals字段讀取對應的值扑馁。設置值時涯呻,向當前線程的threadLocals字段中寫入對應的key和value。ThreadLocalMap中的 Entry 使用弱引用引用了ThreadLocal腻要,并強引用了ThreadLocal對應的線程本地變量副本复罐。當 ThreadLocal 變量被回收后,該映射對應的鍵變?yōu)?null雄家,ThreadLocalMap中對應的 Entry 無法被手動移除效诅,進而使得線程的變量副本被該 Entry引用而無法被回收造成內存泄漏。
ThreadLocalMap與內存泄漏
ThreadLocalMap 的每個 Entry 都是一個對鍵(ThreadLocal
)的弱引用,同時 Entry 又包含了一個對值(當前線程的變量副本)的強引用乱投。當沒有強引用指向 ThreadLocal 變量時咽笼,ThreadLocal可被回收,但Entry中包含的變量副本不能自動釋放戚炫。
防止ThreadLocal內存泄漏
ThreadLocalMap 的 set 方法中剑刑,通過 replaceStaleEntry 方法將所有鍵為 null 的 Entry 的值設置為 null,從而使得該值可被回收双肤。另外叛甫,會在 remove方法、rehash 方法中通過 expungeStaleEntry 方法將鍵為 null 的 Entry 的值設為null并將Entry設置為 null 從而使得該 Entry 及對應的值可被回收杨伙。通過這種方式,ThreadLocal 可防止內存泄漏萌腿。
為了及時的內存回收限匣,可以調用ThreadLocal.remove()
方法主動移除對應的Entry及線程變量副本。
總結
- ThreadLocal 并不解決線程間共享數據的問題
- ThreadLocal 通過隱式的在不同線程內創(chuàng)建獨立實例副本避免了實例線程安全的問題
- 每個線程持有一個 Map 并維護了 ThreadLocal 對象與具體實例的映射毁菱,該 Map 由于只被持有它的線程訪問米死,故不存在線程安全以及鎖的問題
- ThreadLocalMap 的 Entry 對 ThreadLocal 的引用為弱引用,避免了 ThreadLocal 對象無法被回收的問題
- ThreadLocalMap 的 set 方法通過調用 replaceStaleEntry 方法回收鍵為 null 的 Entry 對象的值(即為具體實例)以及 Entry 對象本身從而防止內存泄漏
- ThreadLocal 適用于變量在線程間隔離但在方法間共享的場景
ThreadLocal使用案例
- DataSource中的getConnection()方法
- Spring Security中的ThreadLocalSecurityContextHolderStrategy