凡事有兩面性,知道其優點和劣勢纔是咱們選擇使用與否的判斷依據編程
一、優點 tomcat
A:ThreadLocal 可使得線程獨有的局部變量,在整個線程存活期間內跨越類和實例的進行使用,等同於爲線程內多個實例節點提供了數據bus安全
B:ThreadLocal 存儲的是線程副本,線程消亡後,其內存留的副本數據會隨着gc消亡併發
C:ThreadLocal 從某種角度上來看[線程併發的時候],是犧牲空間來獲取時間的一種操做函數
D:能自然解決線程安全問題,由於是依據線程副本進行的保存,因此其保存的局部變量不會被其餘線程獲取編碼
二、劣勢spa
A:ThreadLocal 佔用了內存空間,由於Threadl爲每一個線程都建立了副本線程
B:使用不當會致使內存泄露,尤爲是對沒有良好編碼習慣的人,尤爲致命接口
C:對線程池[會複用core線程的那種]或者執行耗時較長的線程,慎用!內存
=============================分割線==================================
知道利弊以後說實現原理,ThreadLocal實際上等同於一個2級的hashMap
一、每一個Thread類裏面都有個屬性成員:threadLocals,定義爲:ThreadLocal.ThreadLocalMap,再深刻則爲WeakReference<ThreadLocal<?>>的子類:Entry(ThreadLocal<?> k, Object obj), 實現了對Thread的弱引用和對線程局部變量obj的強引用
二、ThreadLocal<?>存儲的就是threadLocals,由於是以線程爲key的因此線程安全,也就意味着只要代碼所在的實例是屬於同一線程的就能隨時獲取
三、ThreadLocal的get方法就是執行Thread t = Thread.currentThread(); return t.threadLocals,所謂的線程副本也就是這麼來的了
四、注意ThreadLocal在麼有set以前get的是null,除非實現initialValue方法進行初始化。
==================分割線========================================
知道原理以後咱們說說坑,以下1和2都是會致使內存泄露的大坑:
一、無良好編碼習慣的人,會有一個懶操做:就是使用完ThreadLocal退出前不手動執行remove方法進行釋放
固然此舉在不少不少不少狀況下,都是不會有問題的,由於線程是會快速消亡的,因此線程副本的內容也是會跟着gc隨風而去的,
可是,可是有些時候就是不行的,好比由controller引入的tomcat發起的http-bio的線程,這種線程是池模式的,是複用的。但凡一個URL,其http-bio-exec-XX的線程都是存活好久的,由該線程串起來的controller、service和dao都是同一個線程副本,你不釋放,tomcat複用此線程的時候......悲劇就發生了
二、自實現線程池的時候,同上原理,你使用了線程副本又不手動釋放,是打算坐等其隨風而逝麼?
因此,手動remove,在finally裏面手動remove掉使用完的線程副本,!
三、ThreadLocal說的是整個線程內獲取,可是若是你在某個service服務類裏面建立了子線程,怎麼辦?獲取不到了啊就是。
可使用InheritableThreadLocal,這樣子線程也能獲取父線程的局部變量了就,其餘ThreadLocal的子類用處待擴展
============分割線=================
一、擴展如Exchanger 安全的交互兩個線程之間的數據[區分單槽交換slotExchange和多槽交換arenaExchange]
二、SuppliedThreadLocal--JDK 1.8 引入的函數式接口,配合靜態withInitial方法,值得ThreadLocal也能夠進行lamda編程?
例如:ThreadLocal<Long> threadLocal = ThreadLocal.withInitial(System::currentTimeMillis);
System.out.println(threadLocal.get());