日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ThreadLocal介绍以及源码分析

發(fā)布時間:2023/12/20 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ThreadLocal介绍以及源码分析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
ThreadLocal?線程主變量 前面部分引用其他優(yōu)秀博客,后面源碼自己分析的,如有冒犯請私聊我。 用Java語言開發(fā)的同學對?ThreadLocal?應該都不會陌生,這個類的使用場景很多,特別是在一些框架中經(jīng)常用到,比如數(shù)據(jù)庫事務操作,還有MVC框架中數(shù)據(jù)跨層傳遞。這里我們簡要探討下?ThreadLocal?的內(nèi)部實現(xiàn)及可能存在的問題。 首先問自己一個問題,讓自己實現(xiàn)一個這個的功能類的話怎么去做?第一反應就是簡單構(gòu)造一個?Map<Thread, T>?數(shù)據(jù)結(jié)構(gòu),key是?Thread,value就是我們要保存的線程變量?T。我們看下這種設(shè)計有哪些問題:
  • 隨著運行時間越久,存在Map里的Thread越多,當Thread退出時,資源也沒有釋放,存在內(nèi)存泄漏問題
  • Map數(shù)據(jù)因為會被多線程訪問,存在資源競爭,所以還必需對Map做同步安全操作,效率低下
JDK中的?ThreadLocal?精妙的設(shè)計來解決問題上述兩個問題。首先每個Thread(線程)內(nèi)部都有一個Map結(jié)構(gòu)數(shù)據(jù)ThreadLocalMap<ThreadLocal, T>,當我們對線程變量賦值時ThreadLocal.set(T value)時,其實是先獲取當前線程Thread.currentThread())的內(nèi)部屬性字段ThreadLocalMap,然后以當前ThreadLocal為key設(shè)置線程變量值T。這種設(shè)計的精髓是,每個Thread線程都維護一份自己的ThreadLocalMap數(shù)據(jù)結(jié)構(gòu),這樣就解決了上面所述問題中的第二個,不存在競爭條件。 既然每個Thread內(nèi)部都維護一個ThreadLocalMap字典數(shù)據(jù)結(jié)構(gòu),字典的Key值是ThreadLocal,那么當某個ThreadLocal對象不再使用(沒有其它地方再引用)時,每個已經(jīng)關(guān)聯(lián)了此ThreadLocal的線程怎么在其內(nèi)部的ThreadLocalMap里做清除此資源呢?JDK中的ThreadLocalMap又做了一次精彩的表演,它沒有繼承java.util.Map類,而是自己實現(xiàn)了一套專門用來定時清理無效資源的字典結(jié)構(gòu)。其內(nèi)部存儲實體結(jié)構(gòu)Entry<ThreadLocal, T>繼承自java.lan.ref.WeakReference,這樣當ThreadLocal不再被引用時,因為弱引用機制原因,當jvm發(fā)現(xiàn)內(nèi)存不足時,會自動回收弱引用指向的實例內(nèi)存,即其線程內(nèi)部的ThreadLocalMap會釋放其對ThreadLocal的引用從而讓jvm回收ThreadLocal對象。這里是重點強調(diào)下,是回收對ThreadLocal對象,而非整個Entry,所以線程變量中的值T對象還是在內(nèi)存中存在的,所以內(nèi)存泄漏的問題還沒有完全解決。接著分析JDK的實現(xiàn),會發(fā)現(xiàn)在調(diào)用ThreadLocal.get()或者ThreadLocal.set(T)時都會定期執(zhí)行回收無效的Entry操作。所以這就解決了上述問題中的第1個問題。 問題真的都解決了嗎,好像都解決了。因為即沒有競爭資源操作,也不會存在內(nèi)存泄漏。但是細想一下,總感覺哪里不對勁,真的不會存在內(nèi)存溢出(OOM)問題嗎?上面一段的分析中,強調(diào)ThreadLocalMap定期清理內(nèi)部的無效Entry對象,觸發(fā)的條件就是對TrheadLocal執(zhí)行 set,get,remove()等操作時會觸發(fā),但是如果存在這樣的場景,當我們在某個線程上下文中執(zhí)行ThreadLocal.set(T)設(shè)置了一個很大內(nèi)存的數(shù)據(jù)結(jié)構(gòu),然后該ThreadLocal被清除引用回收,之前的線程又一直存活著,則這個大內(nèi)存數(shù)據(jù)對象T是一直不回收的,這里很容易寫個代碼測試出OOM來。怎么解決這個問題呢? Lucene中的org.apache.lucene.util.CloseableThreadLocal類解決了上述特殊場景引起的問題:即解決JDK中因為定期才執(zhí)行無效對象回收的問題。CloseableThreadLocal在內(nèi)部維護了一個ThreadLocal,當執(zhí)行CloseableThreadLocal.set(T)時,內(nèi)部其實只是代理的把值賦給內(nèi)部的ThreadLocal對象,即執(zhí)行ThreadLocal.set(new WeakReference(T))。看到這里應該明白了,這里不是直接存儲T,則是包裝成弱引用對象,目的就是當內(nèi)存不足時,jvm可以回收此對象。但是細心的你會發(fā)現(xiàn)會引入一個新的問題,即當前線程還存活著的時候,因為內(nèi)存不足而回收了弱引用對象,這樣會在下次調(diào)用get()時取不到值返回null,這是不可接受的。所以CloseableThreadLocal在內(nèi)部還創(chuàng)建了一個數(shù)據(jù),WeakHashMap<Thread, T>,當線程只要存活時,則T就至少有一個引用存在,所以不會被提前回收。但是又引入的第2個問題,對WeakHashMap的操作要做同步synchronized限制。你看,所有的東西都不是十全十美的,我們掌握那個平衡點就行了。 ThreadLocal源碼分析 源碼介紹 1、每個線程訪問自己維護的的一份副本 2、ThreadLocal 類內(nèi)部維護了一個私有的字段去跟對應的線程聯(lián)系(例如 userid 或者TranslationId) 這個字段第一次調(diào)用時生成后面調(diào)用不會改變

3、只要這個線程活著而且實例可以被訪問,這個線程會持有這個變量副本的隱性引用,直到線程消亡,被垃圾回收

ThreadLocal get方法 1、根據(jù)當前thread獲取對應的ThreadLocalMap 如果沒有就初始化設(shè)置一個,如果有就返回 ThreadLocalMap 里面維護的Entry存儲的值 1 public T get() { 2 Thread t = Thread.currentThread(); 3 ThreadLocalMap map = getMap(t); 4 if (map != null) { 5 ThreadLocalMap.Entry e = map.getEntry(this); 6 if (e != null) { 7 @SuppressWarnings("unchecked") 8 T result = (T)e.value; 9 return result; 10 } 11 } 12 return setInitialValue(); 13 }

?

?

ThreadLocal 初始化value 倆個步驟 1、在原有的map上設(shè)置值 2、創(chuàng)建一個ThreadLocalMap private T setInitialValue() {T value = initialValue();Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);return value; }

?

?

ThreadLocal rehash 1、先刪除陳舊的Entriy ,如果不能有效的收縮table的長度,而且長度已經(jīng)大于threshold 的0.75(裝載因子)倍了,就直接擴展一倍長度 1 2 /** 3 * Re-pack and/or re-size the table. First scan the entire 4 * table removing stale entries. If this doesn't sufficiently 5 * shrink the size of the table, double the table size. 6 */ 7 private void rehash() { 8 expungeStaleEntries(); 9 10 // Use lower threshold for doubling to avoid hysteresis 11 if (size >= threshold - threshold / 4) 12 resize(); 13 }

如果你也有此類問題,可以一起探討(私聊或者評論),一起不斷完善自己的理解,如果覺得可以歡迎關(guān)注我。

轉(zhuǎn)載于:https://www.cnblogs.com/xushengyong/p/10641827.html

創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎

總結(jié)

以上是生活随笔為你收集整理的ThreadLocal介绍以及源码分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。