真實項目中 ThreadLocal 的妙用

一、什么是 ThreadLocal

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

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

最簡單的使用方式:

類似一次 HTTP 請求線程中,利用 ThreadLocal 存儲 Cookie 對象,進行狀態管理。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 數據庫連接池

比如一次請求線程進來,業務 Dao 需要更新 user 表和 user-detail 表。如果是 new 出兩個數據庫 Connection ,分別不同的 Connection 操作 user 表和 user-detail 表,就無法保證事務。那么數據庫連接池是如何保證的?

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

  • 會從 ThreadLocal 獲取 Connection 對象。如果有,則保證了后面多個數據庫操作共用同一個 Connection ,從而保證了事務。
  • 如果沒有,往 ThreadLocal 新增Connection 對象,并返回到線程
錯誤的做法
public class XXXService {private Connection conn;
}

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

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

繼續講到 Spring 框架,在事務開始時,會給當前線程一個Jdbc Connection,在整個事務過程,都是使用該線程綁定的connection來執行數據庫操作,實現了事務的隔離性。Spring框架里面就是用的ThreadLocal來實現這種隔離

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

Cookie 這樣才能做 Session 等狀態管理。

三、實戰場景

總結一下就是:ThreadLocal 可以讓同一個線程中上下文之間數據共享

在上面章節 二、你熟悉的場景 其實介紹了很多現有場景。那么我這邊具體的實戰場景是什么?

簡單的例子:

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

  1. 一個請求線程,里面有兩個異步小線程,各有一個方法。分別處理 A 或 B 業務
  2. 一種方法是傳遞不可變的入參
  3. 另一種就是 ThreadLocal,放在 ThreadLocal 的入參,會被各個方法共享。而且多個請求線程互不影響
復雜的例子:

一次發貨操作:會根據入參,進行組件化、流程編排話。那么入參會被各個地方用到,而且有些流程組件是異步的(類似 new thread 操作的)。這時候可以定一個 XXContext 上下文:

public class XXContext {private static ThreadLocal<Map<Class<?>, Object>> context = new InheritableThreadLocal<>();/*** 把參數設置到上下文的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);}}/*** 從上下文中,根據類名取出參數*/@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的數據*/public static void clean() {context.remove();}
}

代碼解析:

  • 都是 static 操作,類似 DateUtil 玩法
  • 記得每次請求線程后清理。可以 AOP 去清理,加個注解就行。因為同一個請求線程可能被業務方公用。

(完)

file

轉載于:https://www.cnblogs.com/Alandre/p/11145516.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/247626.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/247626.shtml
英文地址,請注明出處:http://en.pswp.cn/news/247626.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

css之flex布局

flex布局是css3中的重要布局方式&#xff0c;稱為“彈性布局”&#xff0c;每次想到它主要是遇到元素垂直居中、元素寬高自適應的問題&#xff0c;這些問題在flex中都能過簡單設置就解決&#xff0c;它更像是原生APP中的布局操作&#xff0c;布局不必寫N多的盒模型代碼來實現&a…

javascript對URL中的參數進行簡單加密處理

javascript的api本來就支持Base64&#xff0c;因此我們可以很方便的來進行編碼和解碼。 var encodeData window.btoa("namexiaoming&age10")//編碼 var decodeData window.atob(encodeData)//解碼。 下面來個具體的例子來說明如何對url中參數進行轉碼&#xff…

Fibinary Numbers

http://acm.hust.edu.cn/vjudge/contest/view.action?cid30506#problem/V 題意&#xff1a;從右向左&#xff0c;每一個位數&#xff0c;分別表示一個fibonacci數&#xff0c;1表示有&#xff0c;0表示沒有&#xff1b;求兩個數的和&#xff0c;同樣按照這種形式存儲 #include…

移動web開發DRP問題

DPR dpr問題是移動端web開發上需要注意的問題&#xff0c;用大白話說就是&#xff0c;代碼中所設置的像素值實際上是虛擬像素&#xff0c;手機屏幕上的像素實際為物理像素&#xff0c;原始的手機&#xff0c;虛擬像素與物理像素是1:1覆蓋的&#xff0c;但隨著移動端屏幕的技術發…

HTML元素title里面如何換行

在調試代碼的時候我就遇到一個問題&#xff0c;HTML元素title里面通常只顯示一行&#xff0c;那我想要他換行&#xff0c;就是多行顯示&#xff0c;如何實現&#xff1f;JS代碼里面比如Alert里面又該如何換行&#xff1f; 經過我的一番實驗 要實現這種效果有幾種方法&#xff0…

A20 GPIO中斷類型差別結果迥異的問題思考

A20GPIO中斷類型差別結果迥異的問題思考 最近在使用全志A20做開發時&#xff0c;發現在處理中斷的時候&#xff0c;用電平觸發模式&#xff0c;報中斷比較亂&#xff0c;用邊沿觸發則很穩定&#xff0c;不會亂報。筆者感到比較困惑&#xff0c;筆者用電平觸發寫的code如下&…

div內圖片和文字水平垂直居中

大小不固定的圖片、多行文字的水平垂直居中 本文綜述 想必寫css的都知道如何讓單行文字在高度固定的容器內垂直居中&#xff0c;但是您知道或者想過讓行數不固定的文字在高度固定的容器內垂直居中呢&#xff1f;本文將會告訴你如何實現多行文字的垂直居中顯示。 關于圖片垂直居…

sticky-footer實現記錄

sticky-footer是css中的一個經典問題&#xff1a; 當頁面內容超出屏幕&#xff0c;頁腳模塊會像正常頁面一樣&#xff0c;被推到內容下方&#xff0c;需要拖動滾動條才能看到。 而當頁面內容小于屏幕高度&#xff0c;頁腳模塊會固定在屏幕底部&#xff0c;就像是底邊距為零的…

敏友的【敏捷個人】有感(3): 有感于“敏捷個人”討論與練習

2010年我對個人管理進行了自己的一些思考&#xff0c;在2011年提出敏捷個人概念&#xff0c;并且在線上、線下進行了多次交流&#xff0c;在一些大會上也做過分享。現在&#xff0c;已經有很 多IT和非IT的敏友們知道并在踐行敏捷個人&#xff0c;幫助自己更快的成長。我收到大家…

jQuery編寫插件

引言&#xff1a; 在項目中不同頁面經常要用到已經寫好的交互&#xff0c;比如彈窗&#xff0c;比如下拉菜單&#xff0c;比如選項卡&#xff0c;比如刪除... 此時如果每次都把代碼copy一份無疑是一件比較麻煩并且無趣的事情&#xff0c;而且個人認為有些low了&#xff0c;我們…

webstorm中nodejs代碼提示

preferences->languages&frameworks->Node.js and Npm中選擇一個本地的node版本 preferences->languages&frameworks->JavaScript->Libraries 勾選node.js Core 回到代碼

9012教你如何使用gulp4開發項目腳手架

本文將會介紹如何使用gulp4來搭建項目腳手架&#xff0c;如果您還在使用gulp3或更老的版本&#xff0c;您也以通過本文的一些思想將之前的項目進行完善&#xff0c;更新。如果gulp不是你們團隊的重點&#xff0c;也可以移步我的另一篇文章:用 webpack 4.0 擼單頁/多頁腳手架 (j…

nodejs模塊

nodejs模塊遵循commonJS規范&#xff0c;但并不是完全實現規范&#xff0c;主要使用require引入模塊、使用exports導出模塊。 導出屬性或方法 使用exports mouduleA.js&#xff1a; var say function(world){console.info("say: "world); }var sing function(wo…

Array.prototype.slice.call(arguments)

Array.prototype.slice.call(arguments)能將具有length屬性的對象轉成數組&#xff0c;除了IE下的節點集合&#xff08;因為ie下的dom對象是以com對象的形式實現的&#xff0c;js對象與com對象不能進行轉換&#xff09;如&#xff1a;1 var a{length:2,0:first,1:second}; 2 Ar…

動態內存分配導致內存泄漏之處

摘要&#xff1a;舉了幾個動態內存分配過程中&#xff0c;發生內存泄漏的例子 1. 分配了內存&#xff0c;卻沒有及時刪除,導致泄漏 1: void doSomething() 2: { 3: int *pnValue new int; 4: } 2. 為指針變量分配了一個內存&#xff0c;然后又讓指針變量指向其他的值,導致…

nodejs常用模塊-url

URL nodejs中針對url的常用方法。 node下打印url&#xff0c;結果&#xff1a; 引入url模塊 var url require(url) 1、parse方法 將url解析成對象&#xff0c;parse方法原型&#xff1a; url.parse(urlStr[, parseQueryString][, slashesDenoteHost]) 可傳遞三個參數…

常用的javascript設計模式

請堅持 什么是設計模式 百度百科&#xff1a; 設計模式&#xff08;Design pattern&#xff09;是一套被反復使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。 使用設計模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。 毫無疑問&#xff0c;設計模式…

express項目創建

1、安裝項目生成器 cnpm i express-generator -g express4.0以后&#xff0c;express與express-generator包分離&#xff0c;全局使用express命令生成項目骨架時應該安裝express-generator包。 2、生成項目骨架 express server 默認使用的是jade模板&#xff0c;使用ejs模…

設置Jexus開機啟動

把jexus做成系統服務&#xff0c;就像windows服務一樣&#xff0c;可以設置開機啟動就可以了。 第一、進入目錄 /lib/systemd/system/ 第二、新建文件 vi jexus.service 文件內容 [Unit] Descriptionjexus Afternetwork.target[Service] Typeforking ExecStart/usr/jexus/jw…

iOS警告-This block declaration is not a prototype

關于警告 我們定義一個不帶參數的block,通常是如下的方式 1typedefvoid (^UpdateSwichBtnBlock)();在xcode9中會提示一個警告 12This block declaration is not a prototypeInsert ‘void解決方式可以是如下的幾種 1typedefvoid (^UpdateSwichBtnBlock)(void);但是這樣,很多第三…