(精華)2020年12月23日 .NET Core 多線程底層詳解(線程本地存儲)
640
2025-04-01
網上有很多關于ThreadLocal的文章,大部分都提到了多線程之間共享資源的問題。其實ThreadLocal和多線程之間一點關系都沒有。如果有,我怕是它的名字改成ThreadShare是不是更合適呢?開個玩笑。從其名稱ThreadLocal,我們就可以看出他應該是隸屬于線程內部的資源。接下來就詳細說說吧。
我們以前在處理一個Request請求,穿透多個業務方法的時候,如果要共享數據,一般都會搞個Context上下文對象,在多個方法之間進行傳遞,這樣可以解決多個方法之間的數據共享問題。這是很不錯的一種方案,而且這種方案在實際使用的時候效果也非常不錯,因為Context內部我們想放什么,就放什么,想怎么放就怎么放,非常的自由無界。但是這種自由帶來的問題在小流量的請求中是不會有問題的,但是在大流量的請求中,則存在不小的問題,主要在:
1. Context對象,每個請求進來,都會new一個,大流量下,瞬間暴增,由于空間申請操作勢必引發頻繁的young GC, 業務壓力大的時候,full GC也是不可避免的。
2. Context對象,在一個請求終結之后,需要手動釋放。
3. Context對象,存在被請求內部的多線程共享訪問的情形。有線程安全性問題。
上面三個問題,是我隨便列舉的,但是在實際使用中,可能還有更多。但是如果Context的生產和銷毀如果控制的足夠好的話,上面的問題也不是什么問題。
既然Context對象的控制稍顯麻煩,那么JDK有沒有提供什么現成的類庫供我們使用呢? 答案是肯定的,這個對象就是ThreadLocal對象。
說道ThreadLocal對象,我更認為他是在當前請求的上游和下游之間進行數據共享的。那么按照之前的例子說來,如果一個請求穿越多個業務方法,其實數據的共享可以利用ThreadLocal來進行,這樣就無需專門定義一個Context。
首先來看看其內部get實現機制:
/**?*?Returns?the?value?in?the?current?thread's?copy?of?this?*?thread-local?variable.??If?the?variable?has?no?value?for?the?*?current?thread,?it?is?first?initialized?to?the?value?returned?*?by?an?invocation?of?the?{@link?#initialValue}?method.?*?*?@return?the?current?thread's?value?of?this?thread-local?*/public?T?get()?{ ????Thread?t?=?Thread.currentThread(); ????ThreadLocalMap?map?=?getMap(t);????if?(map?!=?null)?{ ????????ThreadLocalMap.Entry?e?=?map.getEntry(this);????????if?(e?!=?null)????????????return?(T)e.value; ????}????return?setInitialValue(); } ThreadLocalMap?getMap(Thread?t)?{????return?t.threadLocals; }private?T?setInitialValue()?{ ????T?value?=?initialValue(); ????Thread?t?=?Thread.currentThread(); ????ThreadLocalMap?map?=?getMap(t);????if?(map?!=?null) ????????map.set(this,?value);????else ????????createMap(t,?value);????return?value; }protected?T?initialValue()?{????return?null; }
從上面源碼可以看出,get操作會從當前Thread上附加的map中進行數據獲取。
再來看看set方法:
/**?*?Sets?the?current?thread's?copy?of?this?thread-local?variable?*?to?the?specified?value.??Most?subclasses?will?have?no?need?to?*?override?this?method,?relying?solely?on?the?{@link?#initialValue}?*?method?to?set?the?values?of?thread-locals.?*?*?@param?value?the?value?to?be?stored?in?the?current?thread's?copy?of?*????????this?thread-local.?*/public?void?set(T?value)?{ ????Thread?t?=?Thread.currentThread(); ????ThreadLocalMap?map?=?getMap(t);????if?(map?!=?null) ????????map.set(this,?value);????else ????????createMap(t,?value); } ThreadLocalMap?getMap(Thread?t)?{????return?t.threadLocals; }void?createMap(Thread?t,?T?firstValue)?{ ????t.threadLocals?=?new?ThreadLocalMap(this,?firstValue); }
從上面源碼看出,首先他會取出當前Thread,然后會將map設置到當前Thread上,用戶可以在這個map上進行數據增刪改查操作。非常巧妙。所以從這里可以看出,一個ThreadLocal對象,即便穿插在多個線程之間,也不會造成資源共享問題,因為他會為每個線程都設置map對象,這也從根本上避免了線程安全問題。
最后,因為ThreadLocal內部的對象為WeakReference,所以不用進行手動釋放,只需要保證一個請求結束,對其內部的引用釋放掉就行了,然后自動會被JVM優先處理掉,根本無需擔心內存泄露問題。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。