SpringBoot中使用?ThreadLocal?進(jìn)行多線程上下文管理及注意事項(xiàng)小結(jié)
前言
在多線程編程中,線程安全是一個(gè)重要的問(wèn)題。Java 提供了多種機(jī)制來(lái)處理線程安全問(wèn)題,其中 ThreadLocal 是一個(gè)非常有用的工具。本文將詳細(xì)介紹 ThreadLocal 的原理及其在多線程上下文管理中的應(yīng)用,并在springboot中使用ThreadLocal保存請(qǐng)求中攜帶的用戶信息。
技術(shù)積累
1.什么是 ThreadLocal
ThreadLocal 是 Java 提供的一個(gè)類,用于在多線程環(huán)境下為每個(gè)線程維護(hù)獨(dú)立的變量副本。這意味著每個(gè)線程都可以獨(dú)立地訪問(wèn)和修改自己的變量副本,而不會(huì)影響其他線程的變量。
2. ThreadLocal 的原理
2.1 線程隔離
每個(gè)線程都有一個(gè) ThreadLocalMap 對(duì)象,該對(duì)象存儲(chǔ)了所有 ThreadLocal 變量的副本。ThreadLocalMap 是 Thread 類的一個(gè)內(nèi)部類,每個(gè)線程實(shí)例都有一個(gè) ThreadLocalMap 實(shí)例。
2.2 存儲(chǔ)機(jī)制
設(shè)置值: 當(dāng)一個(gè)線程調(diào)用 ThreadLocal.set(value) 時(shí),ThreadLocal 會(huì)將值存儲(chǔ)到當(dāng)前線程的 ThreadLocalMap 中。
獲取值: 調(diào)用 ThreadLocal.get() 時(shí),ThreadLocal 會(huì)從當(dāng)前線程的 ThreadLocalMap 中獲取值。
2.3 內(nèi)存管理
弱引用: ThreadLocalMap 使用弱引用(WeakReference)來(lái)存儲(chǔ) ThreadLocal 對(duì)象,以防止內(nèi)存泄漏。
清理: 當(dāng) ThreadLocal 對(duì)象不再被使用時(shí),它可以被垃圾回收,從而避免內(nèi)存泄漏。
3. 使用場(chǎng)景
3.1 用戶會(huì)話管理
在 Web 應(yīng)用中,可以使用 ThreadLocal 存儲(chǔ)用戶會(huì)話信息,確保每個(gè)請(qǐng)求處理線程都能訪問(wèn)到正確的會(huì)話數(shù)據(jù)。
3.2 事務(wù)上下文管理
在數(shù)據(jù)庫(kù)操作中,可以使用 ThreadLocal 存儲(chǔ)事務(wù)上下文,確保每個(gè)線程的操作都在正確的事務(wù)中進(jìn)行。
3.3 線程局部變量
在多線程環(huán)境中,需要每個(gè)線程擁有獨(dú)立的變量副本時(shí),可以使用 ThreadLocal。
4. 示例代碼
以下是一個(gè)簡(jiǎn)單的示例,展示了如何使用 ThreadLocal 來(lái)管理每個(gè)線程的獨(dú)立變量。
public class ThreadLocalExample {
// 創(chuàng)建一個(gè) ThreadLocal 實(shí)例
private static final ThreadLocal threadLocal = new ThreadLocal<>();
public static void main(String[] args) {
// 創(chuàng)建多個(gè)線程
for (int i = 0; i < 5; i++) {
new Thread(() -> {
// 為每個(gè)線程設(shè)置不同的值
threadLocal.set((int) (Math.random() * 100));
try {
// 模擬線程執(zhí)行時(shí)間
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 獲取并打印當(dāng)前線程的值
System.out.println("Thread " + Thread.currentThread().getId() + ": " + threadLocal.get());
// 清除 ThreadLocal 變量,避免內(nèi)存泄漏
threadLocal.remove();
}).start();
}
}
}4.1 關(guān)鍵點(diǎn)
線程隔離: 每個(gè)線程都有獨(dú)立的 ThreadLocal 變量副本。
內(nèi)存管理: 使用 ThreadLocal.remove() 清除不再需要的變量,避免內(nèi)存泄漏。
性能考慮: ThreadLocal 的使用會(huì)增加一定的內(nèi)存開銷,因此在不需要時(shí)應(yīng)及時(shí)清理。
5. 注意事項(xiàng)
5.1 內(nèi)存泄漏
如果不及時(shí)清理 ThreadLocal 變量,可能會(huì)導(dǎo)致內(nèi)存泄漏。因此,建議在使用完 ThreadLocal 變量后調(diào)用 remove() 方法。
5.2 線程池
在使用線程池時(shí),線程可能會(huì)被重用。如果 ThreadLocal 變量沒(méi)有被清理,可能會(huì)導(dǎo)致后續(xù)任務(wù)訪問(wèn)到錯(cuò)誤的數(shù)據(jù)。因此,在使用線程池時(shí),務(wù)必在任務(wù)執(zhí)行完畢后清理 ThreadLocal 變量。
實(shí)戰(zhàn)演示
1. User 類
User 類表示用戶數(shù)據(jù)
/**
* User
* @author senfel
* @version 1.0
* @date 2025/2/18 17:00
*/
@AllArgsConstructor
@NoArgsConstructor
@Data
public class User {
private String id;
private String username;
}2. UserContext 類
UserContext 類使用 ThreadLocal 來(lái)存儲(chǔ)和刪除用戶數(shù)據(jù)。
/**
* UserContext
* @author senfel
* @version 1.0
* @date 2025/2/18 17:01
*/
public class UserContext {
// 創(chuàng)建一個(gè) ThreadLocal 實(shí)例來(lái)存儲(chǔ) User 對(duì)象
private static final ThreadLocal<User> userThreadLocal = new ThreadLocal<>();
// 設(shè)置用戶數(shù)據(jù)
public static void setUser(User user) {
userThreadLocal.set(user);
}
// 獲取用戶數(shù)據(jù)
public static User getUser() {
return userThreadLocal.get();
}
// 刪除用戶數(shù)據(jù)
public static void clearUser() {
userThreadLocal.remove();
}
}3.UserInterceptor 類
UserInterceptor 類用于在請(qǐng)求處理前后設(shè)置和清除 ThreadLocal 中的用戶數(shù)據(jù)。
/**
* UserInterceptor
* @author senfel
* @version 1.0
* @date 2025/2/18 17:03
*/
@Component
public class UserInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 模擬從請(qǐng)求中獲取用戶數(shù)據(jù)
String userId = request.getParameter("userId");
String username = request.getParameter("username");
if (userId == null || username == null) {
response.getWriter().write("User ID and Username are required.");
return false;
}
// 創(chuàng)建 User 對(duì)象并存儲(chǔ)在 ThreadLocal 中
User user = new User(userId, username);
UserContext.setUser(user);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 清除 ThreadLocal 中的用戶數(shù)據(jù),避免內(nèi)存泄漏
UserContext.clearUser();
}
}4.配置攔截器
在 Spring Boot 中配置攔截器,使其在請(qǐng)求處理前后執(zhí)行。
/**
* WebConfig
* @author senfel
* @version 1.0
* @date 2025/2/18 17:10
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private UserInterceptor userInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(userInterceptor).addPathPatterns("/**");
}5.實(shí)戰(zhàn)測(cè)試
隨便訪問(wèn)一個(gè)路徑,都會(huì)從請(qǐng)求中獲取用戶信息并放入local,在執(zhí)行控制器結(jié)束后會(huì)清理掉數(shù)據(jù)。


總結(jié)
ThreadLocal 是一個(gè)非常強(qiáng)大的工具,可以幫助我們?cè)诙嗑€程環(huán)境中管理線程局部變量。通過(guò)合理使用 ThreadLocal,可以有效地避免線程安全問(wèn)題,提高程序的并發(fā)性能和穩(wěn)定性。我們可以在 Spring Boot 應(yīng)用中安全地存儲(chǔ)和管理每個(gè)請(qǐng)求的用戶數(shù)據(jù),并通過(guò)顯式地清理 ThreadLocal 變量,可以有效避免內(nèi)存泄漏問(wèn)題。
到此這篇關(guān)于SpringBoot中使用 ThreadLocal 進(jìn)行多線程上下文管理及其注意事項(xiàng)的文章就介紹到這了,更多相關(guān)SpringBoot ThreadLocal多線程上下文管理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- springboot在filter中如何用threadlocal存放用戶身份信息
- SpringBoot中的ThreadLocal保存請(qǐng)求用戶信息的實(shí)例demo
- springboot登錄攔截器+ThreadLocal實(shí)現(xiàn)用戶信息存儲(chǔ)的實(shí)例代碼
- SpringBoot ThreadLocal 簡(jiǎn)單介紹及使用詳解
- SpringBoot+ThreadLocal+AbstractRoutingDataSource實(shí)現(xiàn)動(dòng)態(tài)切換數(shù)據(jù)源
- Springboot公共字段填充及ThreadLocal模塊改進(jìn)方案
- SpringBoot ThreadLocal實(shí)現(xiàn)公共字段自動(dòng)填充案例講解
- SpringBoot通過(guò)ThreadLocal實(shí)現(xiàn)登錄攔截詳解流程
- springboot 使用ThreadLocal的實(shí)例代碼
相關(guān)文章
Springboot如何基于assembly服務(wù)化實(shí)現(xiàn)打包
這篇文章主要介紹了Springboot如何基于assembly服務(wù)化實(shí)現(xiàn)打包,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06
一文教你學(xué)會(huì)如何使用MyBatisPlus
本篇文章,我們通過(guò) MyBatis Plus 來(lái)對(duì)一張表進(jìn)行 CRUD 操作,來(lái)看看是如何簡(jiǎn)化我們開發(fā)的,文中通過(guò)代碼示例介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-05-05
myeclipse導(dǎo)出可運(yùn)行jar包簡(jiǎn)介
這篇文章主要介紹了myeclipse導(dǎo)出可運(yùn)行jar包簡(jiǎn)介,具有一定參考價(jià)值,需要的朋友可以了解下。2017-11-11
Spring中自帶的@Schedule實(shí)現(xiàn)自動(dòng)任務(wù)的過(guò)程解析
這篇文章主要介紹了關(guān)于Spring中自帶的@Schedule實(shí)現(xiàn)自動(dòng)任務(wù),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-06-06
詳解spring集成mina實(shí)現(xiàn)服務(wù)端主動(dòng)推送(包含心跳檢測(cè))
本篇文章主要介紹了詳解spring集成mina實(shí)現(xiàn)服務(wù)端主動(dòng)推送(包含心跳檢測(cè)),具有一定的參考價(jià)值,與興趣的可以了解一下2017-09-09

