一逊笆、ThreadLocal簡介
? ? ? ? 多線程訪問同一個共享變量時經(jīng)常容易產(chǎn)生并發(fā)問題,為了保證線程安全岂傲,就需要一定的同步機(jī)制难裆,除了常見的加鎖處理以外,JDK還提供了ThreadLocal镊掖,即本地變量副本乃戈。當(dāng)創(chuàng)建了一個ThreadLocal變量,那么訪問這個變量的每一個線程都會有該變量的一個本地副本堰乔,當(dāng)多個線程操作該變量時偏化,實(shí)際操作的是自己的本地內(nèi)存中保存的變量脐恩,從而避免了線程安全問題镐侯。
二、ThreadLocal使用示例
? ? ? ? 示例代碼:
? ? ? ? 從運(yùn)行結(jié)果可以看出threadA和threadB調(diào)用的ThreadLocal.set設(shè)置的是線程本地內(nèi)存中的一個副本,這個副本在線程間是隔離的苟翻,彼此不可見韵卤。
三、ThreadLocal實(shí)現(xiàn)原理
? ? ? ? 從Thread源碼中可以看到崇猫,Thread有兩個變量threadLocals和inheritableThreadLocals沈条,而且都是ThreadLocal.ThreadLocalMap類型的變量:
? ? ? ? 即每個Thread都包含自己的ThreadLocalMap:
? ? ? ? 由ThreadLocalMap可以發(fā)現(xiàn),ThreadLocalMap是ThreadLocal的靜態(tài)內(nèi)部類诅炉,真正起到保存線程私有數(shù)據(jù)的正是這個ThreadLocalMap蜡歹,而ThreadLocal只是在外面包了一層,對外提供統(tǒng)一入口涕烧,下面分析一下ThreadLocalMap的源碼:
? ? ? ? 可以發(fā)現(xiàn)ThreadLocalMap當(dāng)中有一個名為table的Entry類型的數(shù)組月而,且Entry的鍵是ThreadLocal的弱引用(WeakReference)。而ThreadLocalMap的初始化议纯,實(shí)際上是對Entry數(shù)組的初始化:
? ? ? ? 到這里大概就猜測到ThreadLocal對各自線程本地變量副本的操作實(shí)際上是對ThreadLocalMap的操作父款,我們驗(yàn)證一下,看幾個ThreadLocal常用的api:
? ? ? ? (1)ThreadLocal.get
? ? ? ? ? ?ThreadLocal中的get方法瞻凤,首先會獲取當(dāng)前線程的ThreadLocalMap憨攒,以ThreadLocal本身作為key去ThreadLocalMap中查找,如果存在不為空Entry阀参,就返回Entry.value肝集,否則就會初始化ThreadLocalMap并返回初始化后的默認(rèn)值,初始化源碼如下:
? ? ? ? (2)ThreadLocal.set
? ? ? ? 先獲取當(dāng)前線程蛛壳,并取出當(dāng)前線程的ThreadLocalMap包晰,如果ThreadLocalMap不為空,則直接以key為ThreadLocal引用為鍵炕吸,value為關(guān)聯(lián)值進(jìn)行存儲伐憾;如果ThreadLocalMap為空,則會先創(chuàng)建ThreadLocalMap再進(jìn)行存儲赫模。
? ? ? ? (3)ThreadLocal.remove
? ? ? ? 先根據(jù)當(dāng)前線程獲取到該線程的TreadLocalMap树肃,如果ThreadLocalMap存在,就調(diào)用ThreadLocalMap.remove方法刪除當(dāng)前線程中指定的ThreadLocal實(shí)例的本地變量瀑罗。
? ? ? ? 綜上胸嘴,可以得出ThreadLocal存儲的數(shù)據(jù)結(jié)構(gòu):
四、ThreadLocal內(nèi)存泄露問題
? ? ? ? ThreadLocalMap是Thread類的成員變量斩祭,Thread銷毀時劣像,ThreadLocalMap也會隨之銷毀,即ThreadLocalMap的生命周期與Thread綁定摧玫。而ThreadLocalMap內(nèi)部維護(hù)了一個Enrty數(shù)組耳奕,而Entry數(shù)組的鍵為ThreadLocal的弱引用,弱引用的特點(diǎn)是食铐,JVM將會在下一次GC的時候清理掉弱引用丐谋,而ThreadLocalMap的value為對象的強(qiáng)引用,且ThreadLocalMap生命周期與Thread相同奈懒,當(dāng)GC發(fā)生時芍躏,會出現(xiàn)key被清理邪乍,而value還存活的情況,導(dǎo)致value無法訪問到对竣,無法被回收庇楞,此時就會發(fā)生內(nèi)存泄露。
強(qiáng)否纬、軟姐刁、弱、虛引用內(nèi)容可以參考深入理解Java虛擬機(jī)(三)—— 垃圾收集器與內(nèi)存分配策略
? ? ? ? 解決辦法:在將ThreadLocal設(shè)置為空前烦味,執(zhí)行remove方法聂使,將key為空的鍵值對也清空。
? ? ? ??