真實項目中 ThreadLocal 的妙用

一互艾、什么是 ThreadLocal

ThreadLocal 提供了線程的局部變量蚯嫌,每個線程都可以通過 set() 和 get() 來對這個局部變量進行操作,但不會和其他線程的局部變量沖突蜂嗽,實現(xiàn)了線程間的據(jù)隔離筑凫。

簡單講:一個獲取用戶的請求線程 A嫌变,如果向 ThreadLocal 填充變量 AValue(只能被線程 A 操作)吨艇,該變量對其他獲取用戶的請求線程 B、C...是隔離的.

最簡單的使用方式:

類似一次 HTTP 請求線程中腾啥,利用 ThreadLocal 存儲 Cookie 對象东涡,進行狀態(tài)管理。set Cookie:

private ThreadLocal httpThreadLocal = new ThreadLocal();

httpThreadLocal.set(“Cookie: sid=13420771402233”)

上面存儲格式是 String 倘待,實際場景存儲的是具體的對象疮跑。在這次 HTTP 請求過程中,任何時候都可以獲取 Cookie 凸舵。獲取方式很簡單 get Cookie:

String cookieValue = (String) httpThreadLocal.get();

Thread 與 ThreadLocal 對象引用關系圖

二祖娘、你熟悉的場景

2.1 數(shù)據(jù)庫連接池

比如一次請求線程進來,業(yè)務 Dao 需要更新 user 表和 user-detail 表啊奄。如果是 new 出兩個數(shù)據(jù)庫 Connection 渐苏,分別不同的 Connection 操作 user 表和 user-detail 表,就無法保證事務菇夸。那么數(shù)據(jù)庫連接池是如何保證的琼富?

答案是:利用 ThreadLocal 存儲唯一 Connection 對象。每次請求線程庄新,pool.getConnection 獲取連接的時候都會這樣操作:

  • 會從 ThreadLocal 獲取 Connection 對象鞠眉。如果有薯鼠,則保證了后面多個數(shù)據(jù)庫操作共用同一個 Connection ,從而保證了事務械蹋。
  • 如果沒有出皇,往 ThreadLocal 新增Connection 對象,并返回到線程
錯誤的做法
public class XXXService {

    private Connection conn;
}

因為 conn 是線程不安全的哗戈。這樣會導致多個請求公用一個連接郊艘。請求量很大的情況下,延遲各種谱醇。你懂暇仲。

因此,使用 ThreadLocal 保證每個請求線程的 Connection 是唯一的副渴。即每個線程有自己的連接。

繼續(xù)講到 Spring 框架全度,在事務開始時煮剧,會給當前線程一個Jdbc Connection,在整個事務過程,都是使用該線程綁定的connection來執(zhí)行數(shù)據(jù)庫操作将鸵,實現(xiàn)了事務的隔離性勉盅。Spring框架里面就是用的ThreadLocal來實現(xiàn)這種隔離

2.2 HTTP Cookie

比如你訪問百度、我訪問百度顶掉,會有不同 Cookie 草娜。而且你不能訪問我的 Cookie,我也不能痒筒。顧名思義宰闰,使用 ThreadLocal 保證每個 HTTP 請求線程的 Cookie 是唯一的。

Cookie 這樣才能做 Session 等狀態(tài)管理簿透。

三移袍、實戰(zhàn)場景

總結(jié)一下就是:ThreadLocal 可以讓同一個線程中上下文之間數(shù)據(jù)共享

在上面章節(jié) 二、你熟悉的場景 其實介紹了很多現(xiàn)有場景老充。那么我這邊具體的實戰(zhàn)場景是什么葡盗?

簡單的例子:

適用滿足這兩個條件的場景:1.每個線程獨有的一些信息,2.這些信息又會在多個方法或類中用到啡浊。

  1. 一個請求線程觅够,里面有兩個異步小線程,各有一個方法巷嚣。分別處理 A 或 B 業(yè)務
  2. 一種方法是傳遞不可變的入?yún)?/li>
  3. 另一種就是 ThreadLocal喘先,放在 ThreadLocal 的入?yún)ⅲ瑫桓鱾€方法共享涂籽。而且多個請求線程互不影響
復雜的例子:

一次發(fā)貨操作:會根據(jù)入?yún)⑵凰睿M行組件化、流程編排話。那么入?yún)桓鱾€地方用到树枫,而且有些流程組件是異步的(類似 new thread 操作的)直焙。這時候可以定一個 XXContext 上下文:

public class XXContext {
    
    private static ThreadLocal<Map<Class<?>, Object>> context = new InheritableThreadLocal<>();
    
    /**
     * 把參數(shù)設置到上下文的Map中
     */
    public static void put(Object obj) {
        Map<Class<?>, Object> map = context.get();
        if (map == null) {
            map = new HashMap<>();
            context.set(map);
        }
        if (obj instanceof Enum) {
            map.put(obj.getClass().getSuperclass(), obj);
        } else {
            map.put(obj.getClass(), obj);
        }
    }
    
    /**
     * 從上下文中,根據(jù)類名取出參數(shù)
     */
    @SuppressWarnings("unchecked")
    public static <T> T get(Class<T> c) {
        Map<Class<?>, Object> map = context.get();
        if (map == null) {
            return null;
        }
        return (T) map.get(c);
    }
    
    /**
     * 清空ThreadLocal的數(shù)據(jù)
     */
    public static void clean() {
        context.remove();
    }
}

代碼解析:

  • 都是 static 操作砂轻,類似 DateUtil 玩法
  • 記得每次請求線程后清理奔誓。可以 AOP 去清理搔涝,加個注解就行厨喂。因為同一個請求線程可能被業(yè)務方公用。

(完)

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末庄呈,一起剝皮案震驚了整個濱河市蜕煌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌诬留,老刑警劉巖斜纪,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異文兑,居然都是意外死亡盒刚,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門绿贞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來因块,“玉大人,你說我怎么就攤上這事籍铁∥猩希” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵寨辩,是天一觀的道長吓懈。 經(jīng)常有香客問我,道長靡狞,這世上最難降的妖魔是什么耻警? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮甸怕,結(jié)果婚禮上甘穿,老公的妹妹穿的比我還像新娘。我一直安慰自己梢杭,他們只是感情好温兼,可當我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著武契,像睡著了一般募判。 火紅的嫁衣襯著肌膚如雪荡含。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天届垫,我揣著相機與錄音释液,去河邊找鬼。 笑死装处,一個胖子當著我的面吹牛误债,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播妄迁,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼寝蹈,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了登淘?” 一聲冷哼從身側(cè)響起箫老,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎黔州,沒想到半個月后槽惫,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡辩撑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了仿耽。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片合冀。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖项贺,靈堂內(nèi)的尸體忽然破棺而出君躺,到底是詐尸還是另有隱情,我是刑警寧澤开缎,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布棕叫,位于F島的核電站,受9級特大地震影響奕删,放射性物質(zhì)發(fā)生泄漏俺泣。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一完残、第九天 我趴在偏房一處隱蔽的房頂上張望伏钠。 院中可真熱鬧,春花似錦谨设、人聲如沸熟掂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赴肚。三九已至素跺,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間誉券,已是汗流浹背指厌。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留横朋,地道東北人仑乌。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像琴锭,于是被迫代替她去往敵國和親晰甚。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內(nèi)容