1. 程式人生 > >真實專案中 ThreadLocal 的妙用

真實專案中 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 去清理,加個註解就行。因為同一個請求執行緒可能被業務方公用。

(完)

相關推薦

真實專案 ThreadLocal

一、什麼是 ThreadLocal ThreadLocal 提供了執行緒的區域性變數,每個執行緒都可以通過 set() 和 get() 來對這個區域性變數進行操作,但不會和其他執行緒的區域性變數衝突,實現了執行緒間的據隔離。 簡單講:一個獲取使用者的請求執行緒 A,如果向 ThreadLocal 填充變數 A

KeyPath在Swift

原文連結: The power of key paths in Swift 自從swift剛開始就被設計為是編譯時安全和靜態型別後,它就缺少了那種我麼經常在執行時語言中的動態特性,比如Object-C, Ruby和JavaScript。舉個例子,在Object-C中,我們可以很輕易的動態去獲取一個物件的任意

C語言異或運算在程式設計

異或運算子^也稱XOR運算子。它的規則是若參加運算的兩個二進位同號,則結果為0(假);異號則為1(真)。即0 ^ 0=0,0 ^ 1=1,1 ^ 1=0。 性質: (1) 一個數與1異或會翻轉 (2) 一個數與0異或保持不變 (3) 一個數異或它本身等於0

switchHosts! 專案人人都的神器

SwitchHosts! 是一個管理、切換多個 hosts 方案的工具。 不管是開發人員、測試人員、PD、、、都離不開switchHosts! 它是一個免費開源軟體。日常開發工作

jQuery select 全國城市三級聯動省市區(專案修改頁面到了)

jQuery select 全國城市三級聯動 在專案中修改頁面會用到,之前用$.filter()函式不行,換成$.each()就可以啦 附程式碼: $(function () { _init_area(); setTimeout(functi

Android省市區三級聯動滾輪選擇(真實專案提取出來的元件)

最近專案要做一個,類似淘寶手機客戶端的,選擇收貨地址的三級聯動滾動選擇元件,下面是它的大致介面截圖: 在IOS中有個叫UIPickerView的選擇器,並且在dataSource中定義了UIPickerView的資料來源和定製內容,所以用只要熟悉它的基本用法,要實現這麼

專案統計報表到echarts圖表,備份一下

首先引入js檔案 <script src="/assets/js/echarts.min.js" charset="utf-8"></script> 觸發事件 <button onclick="javascript:que

談動態代理在解決記憶體洩露

記憶體洩露在Android開發中很常見,每次產品上線之前都要集中解決記憶體洩露問題,有的問題很明顯,有的藏的很深,解決起來要頗費一番功夫,不過總的思路都是一樣,就是切斷引用鏈,讓資源在該釋放的時候能被及時釋放。 我們先看一個記憶體洩露的案例: publi

雙引號在搜尋

有時候我們想輸入一個完整的片語,卻不想搜尋結果不盡人意,例如,輸入:VC++ 教程會返回如圖1的結果: (圖1) 這時我們要藉助Google中的一個雙引號功能輸入:"VC++ 教程" (圖2) 瞧,"VC++ 教程"這個片語被準確地顯示在搜尋結果中。所以平常要搜尋片語時

getchar()在C程式

 getchar()在C程式中的功能是接收一個字元,當我們在連續輸入字元的時候getchar()會給你意想不到的效果。下面是net小夥做的一些測試: 首先看下面的這個程式: 1 #include<stdio.h> 2 #include<stdl

【一起學設計模式】觀察者模式實戰:真實專案屢試不爽的瓜娃EventBus到底如何實現觀察者模式的?

申明 本文章首發自本人公眾號:壹枝花算不算浪漫,如若轉載請標明來源! 感興趣的小夥伴可關注個人公眾號:壹枝花算不算浪漫 22.jpg 前言 之前出過一個設計模式的系列文章,這些文章和其他講設計模式的文章 有些不同 文章沒有拘泥於講解設計模式的原理,更多的是梳理工作中實際用到的一些設計模式,並提取出對應業務模

深入淺出 Javascriptapply、call、bind

com alt apply all 如何使用 name 深入 期待 單體模式   網上文章雖多,大多復制粘貼,且晦澀難懂,我希望能夠通過這篇文章,能夠清晰的提升對apply、call、bind的認識,並且列出一些它們的妙用加深記憶。  apply、call   在 ja

C語言在linux內核do while(0)之法

pos turn jsb world div fprintf cpp efault code 為什麽說do while(0) 妙?由於它的確就是妙,並且在linux內核中實現是相當的妙,我們來看看內核中的相關代碼: #define db_error(fmt, ..

asp.net coreIHttpContextAccessor和HttpContextAccessor的

class sys image 只需要 iap href build .com bubuko 分享一篇文章,關於asp.net core中httpcontext的拓展。 現在,試圖圍繞HttpContext.Current構建你的代碼真的不是一個好主意,但是我想如果你

Python__name__屬性的

-s mod 特點 ont __main__ span pytho 執行 自己 在Python中,每一個module文件都有一個built-in屬性:__name__,這個__name__有如下特點: 1 如果這個module文件是被別的文件導入的,那麽,該__name__

delphi OutputDebugString 函數的(轉載)

ins dbgview rect blank read con fine eve bsp 原文地址 https://www.peganza.com/delphi-and-outputdebugstring.html Ever wanted to monitor your

賦值語句||與&&的理解與

一、 宣告變數a a = 1 || 100; a = 0 || 100; 1、上述程式碼在JavaScript(弱型別的解釋型語言)中,結果如下: a = 1 || 100;  //1 a = 0 || 100;  //100 2、上述程式碼在C、Java等(強型別編譯型語言)中,結果如下:

分享JavaWebfilter過濾器的案例 - 髒話過濾/編碼過濾/程式碼過濾

  案例1. 利用Servlet的過濾器Filter進行完成髒話過濾 package cn.javabs.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servle

所謂的網頁爬蟲java程式碼來實現,此程式碼適合在maven專案使用使用,因為,程式碼的類所對應的依賴可以讓maven下載。

//獲得httpClient物件 CloseableHttpClient httpClient = HttpClients.createDefault(); //url公司域名隨便 String url = "https://www.baidu.co

delphi OutputDebugString 函式的(轉載)

原文地址 https://www.peganza.com/delphi-and-outputdebugstring.html Ever wanted to monitor your Delphi application in realtime, and be able to view log message