欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

關(guān)于ThreadLocal的用法和說(shuō)明及注意事項(xiàng)

 更新時(shí)間:2024年05月11日 11:00:51   作者:二旬老者丶  
這篇文章主要介紹了關(guān)于ThreadLocal的用法和說(shuō)明及注意事項(xiàng),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

ThreadLocal

ThreadLocal是用于解決Java并發(fā)安全性問(wèn)題的一個(gè)類(lèi)。

其主要作用是防止不同線(xiàn)程中的數(shù)據(jù)沖突。

原理圖

如下: 

原理說(shuō)明

創(chuàng)建一個(gè)ThreadLocal<V>類(lèi)的對(duì)象,默認(rèn)會(huì)在每一個(gè)線(xiàn)程中都開(kāi)啟一小片區(qū)域,該片區(qū)域可以理解為kay value格式的(實(shí)質(zhì)上是在Thread中有內(nèi)部類(lèi)ThreadLocalMap,每聲明了一個(gè)ThreadLocal,就相當(dāng)于在這個(gè)ThreadLocalMap中設(shè)置了一個(gè)<key,value>,因?yàn)榫€(xiàn)程是相互獨(dú)立的,所以ThreadLocalMap也是獨(dú)立的),ThreadLocalMap中以ThreadLocal實(shí)例引用的變量名為key,V為value。

每一個(gè)V都是線(xiàn)程獨(dú)有的!

使用

ThreadLocal類(lèi)接口很簡(jiǎn)單,只有4個(gè)方法:

• void set(Object value)

  • 設(shè)置當(dāng)前線(xiàn)程的線(xiàn)程局部變量的值。

• public Object get()

  • 該方法返回當(dāng)前線(xiàn)程所對(duì)應(yīng)的線(xiàn)程局部變量。

• public void remove()

  • 將當(dāng)前線(xiàn)程局部變量的值刪除,目的是為了減少內(nèi)存的占用,該方法是JDK 5.0新增的方法。
  • 需要指出的是,當(dāng)線(xiàn)程結(jié)束后,對(duì)應(yīng)該線(xiàn)程的局部變量將自動(dòng)被垃圾回收,所以顯式調(diào)用該方法清除線(xiàn)程的局部變量并不是必須的操作,但它可以加快內(nèi)存回收的速度。

• protected Object initialValue()

  • 返回該線(xiàn)程局部變量的初始值,該方法是一個(gè)protected的方法,顯然是為了讓子類(lèi)覆蓋而設(shè)計(jì)的。
  • 這個(gè)方法是一個(gè)延遲調(diào)用方法,在線(xiàn)程第1次調(diào)用get()或set(Object)時(shí)才執(zhí)行,并且僅執(zhí)行1次。
  • ThreadLocal中的缺省實(shí)現(xiàn)直接返回一個(gè)null。

實(shí)例!

public final static ThreadLocal<String> threadLocal= new ThreadLocal<String>();

threadLocal代表一個(gè)能夠存放String類(lèi)型的ThreadLocal對(duì)象。

此時(shí)不論什么一個(gè)線(xiàn)程能夠并發(fā)訪(fǎng)問(wèn)這個(gè)變量,對(duì)它進(jìn)行寫(xiě)入、讀取操作,都是線(xiàn)程安全的。

注意?。?!

ThreadLocal如果應(yīng)用不妥當(dāng)會(huì)導(dǎo)致內(nèi)存泄漏。

先來(lái)說(shuō)下什么是內(nèi)存泄漏和內(nèi)存溢出,內(nèi)存泄漏是指某個(gè)變量申請(qǐng)了內(nèi)存的資源,但是引用釋放了,這樣就導(dǎo)致占用著內(nèi)存卻不能訪(fǎng)問(wèn)到(俗話(huà)叫占著茅坑不拉屎!);

內(nèi)存溢出是指某個(gè)變量在申請(qǐng)內(nèi)存空間資源的時(shí)候需要的空間大于實(shí)際的空間,即為內(nèi)存空間不足了(人太多坑不夠了?。?/p>

如圖解:

當(dāng)寫(xiě)下 o=null時(shí),只是表示o不再指向堆中object的對(duì)象實(shí)例,不代表這個(gè)對(duì)象實(shí)例不存在了。

下面來(lái)說(shuō)明下Java中創(chuàng)建引用的幾種方法

  • 強(qiáng)引用就是指在程序代碼之中普遍存在的,類(lèi)似“Object obj=new Object()”這類(lèi)的引用,只要強(qiáng)引用還存在,垃圾收集器永遠(yuǎn)不會(huì)回收掉被引用的對(duì)象實(shí)例。
  • 軟引用是用來(lái)描述一些還有用但并非必需的對(duì)象。對(duì)于軟引用關(guān)聯(lián)著的對(duì)象,在系統(tǒng)將要發(fā)生內(nèi)存溢出異常之前,將會(huì)把這些對(duì)象實(shí)例列進(jìn)回收范圍之中進(jìn)行第二次回收。如果這次回收還沒(méi)有足夠的內(nèi)存,才會(huì)拋出內(nèi)存溢出異常。在JDK 1.2之后,提供了SoftReference類(lèi)來(lái)實(shí)現(xiàn)軟引用。
  • 弱引用也是用來(lái)描述非必需對(duì)象的,但是它的強(qiáng)度比軟引用更弱一些,被弱引用關(guān)聯(lián)的對(duì)象實(shí)例只能生存到下一次垃圾收集發(fā)生之前。當(dāng)垃圾收集器工作時(shí),無(wú)論當(dāng)前內(nèi)存是否足夠,都會(huì)回收掉只被弱引用關(guān)聯(lián)的對(duì)象實(shí)例。在JDK 1.2之后,提供了WeakReference類(lèi)來(lái)實(shí)現(xiàn)弱引用。
  • 虛引用也稱(chēng)為幽靈引用或者幻影引用,它是最弱的一種引用關(guān)系。一個(gè)對(duì)象實(shí)例是否有虛引用的存在,完全不會(huì)對(duì)其生存時(shí)間構(gòu)成影響,也無(wú)法通過(guò)虛引用來(lái)取得一個(gè)對(duì)象實(shí)例。為一個(gè)對(duì)象設(shè)置虛引用關(guān)聯(lián)的唯一目的就是能在這個(gè)對(duì)象實(shí)例被收集器回收時(shí)收到一個(gè)系統(tǒng)通知。在JDK 1.2之后,提供了PhantomReference類(lèi)來(lái)實(shí)現(xiàn)虛引用。

這里只舉一個(gè)軟引用的例子:

SoftReference<String> ref = new SoftReference<String>("Hello world");

這樣就設(shè)置了 ref 對(duì)內(nèi)存中 "Hello world"的軟引用。

ThreadLocal產(chǎn)生內(nèi)存泄漏的原因

根據(jù)我們前面對(duì)ThreadLocal的分析,我們可以知道每個(gè)Thread 擁有一個(gè) ThreadLocalMap,這個(gè)映射表的 key 是 ThreadLocal實(shí)例本身,value 是真正需要存儲(chǔ)的 Object,也就是說(shuō) ThreadLocal 本身并不存儲(chǔ)值,它只是作為一個(gè) key 來(lái)讓線(xiàn)程從 ThreadLocalMap 獲取 value。

仔細(xì)觀察ThreadLocalMap,這個(gè)map是使用 ThreadLocal 的弱引用作為 Key 的,弱引用的對(duì)象在 GC 時(shí)會(huì)被回收。

圖中的虛線(xiàn)表示弱引用。

這樣,當(dāng)把threadlocal變量置為null以后,沒(méi)有任何強(qiáng)引用指向threadlocal實(shí)例,所以threadlocal將會(huì)被gc回收。這樣一來(lái),ThreadLocalMap中就會(huì)出現(xiàn)key為null的Entry,就沒(méi)有辦法訪(fǎng)問(wèn)這些key為null的Entry的value,如果當(dāng)前線(xiàn)程再遲遲不結(jié)束的話(huà),這些key為null的Entry的value就會(huì)一直存在一條強(qiáng)引用鏈:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value,而這塊value永遠(yuǎn)不會(huì)被訪(fǎng)問(wèn)到了,所以存在著內(nèi)存泄露。

只有當(dāng)前thread結(jié)束以后,current thread就不會(huì)存在棧中,強(qiáng)引用斷開(kāi),Current Thread、Map value將全部被GC回收。最好的做法是不在需要使用ThreadLocal變量后,都調(diào)用它的remove()方法,清除數(shù)據(jù)。

所以回到我們前面的實(shí)驗(yàn)場(chǎng)景,場(chǎng)景3中,雖然線(xiàn)程池里面的任務(wù)執(zhí)行完畢了,但是線(xiàn)程池里面的5個(gè)線(xiàn)程會(huì)一直存在直到JVM退出,我們set了線(xiàn)程的localVariable變量后沒(méi)有調(diào)用localVariable.remove()方法,導(dǎo)致線(xiàn)程池里面的5個(gè)線(xiàn)程的threadLocals變量里面的new LocalVariable()實(shí)例沒(méi)有被釋放。

其實(shí)考察ThreadLocal的實(shí)現(xiàn),我們可以看見(jiàn),無(wú)論是get()、set()在某些時(shí)候,調(diào)用了expungeStaleEntry方法用來(lái)清除Entry中Key為null的Value,但是這是不及時(shí)的,也不是每次都會(huì)執(zhí)行的,所以一些情況下還是會(huì)發(fā)生內(nèi)存泄露。只有remove()方法中顯式調(diào)用了expungeStaleEntry方法。

從表面上看內(nèi)存泄漏的根源在于使用了弱引用,但是另一個(gè)問(wèn)題也同樣值得思考:為什么使用弱引用而不是強(qiáng)引用?

下面我們分兩種情況討論

  • key 使用強(qiáng)引用:對(duì)ThreadLocal對(duì)象實(shí)例的引用被置為null了,但是ThreadLocalMap還持有這個(gè)ThreadLocal對(duì)象實(shí)例的強(qiáng)引用,如果沒(méi)有手動(dòng)刪除,ThreadLocal的對(duì)象實(shí)例不會(huì)被回收,導(dǎo)致Entry內(nèi)存泄漏。
  • key 使用弱引用:對(duì)ThreadLocal對(duì)象實(shí)例的引用被被置為null了,由于ThreadLocalMap持有ThreadLocal的弱引用,即使沒(méi)有手動(dòng)刪除,ThreadLocal的對(duì)象實(shí)例也會(huì)被回收。value在下一次ThreadLocalMap調(diào)用set,get,remove都有機(jī)會(huì)被回收。

比較兩種情況,我們可以發(fā)現(xiàn):

由于ThreadLocalMap的生命周期跟Thread一樣長(zhǎng),如果都沒(méi)有手動(dòng)刪除對(duì)應(yīng)key,都會(huì)導(dǎo)致內(nèi)存泄漏,但是使用弱引用可以多一層保障。

因此,ThreadLocal內(nèi)存泄漏的根源是:

由于ThreadLocalMap的生命周期跟Thread一樣長(zhǎng),如果沒(méi)有手動(dòng)刪除對(duì)應(yīng)key就會(huì)導(dǎo)致內(nèi)存泄漏,而不是因?yàn)槿跻谩?/p>

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論