最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot實現(xiàn)ThreadLocal父子線程傳值的幾種方式

 更新時間:2025年12月04日 08:21:24   作者:風象南  
ThreadLocal作為Java中重要的線程本地變量機制,為我們提供了在單個線程內(nèi)存儲數(shù)據(jù)的便利,但是,當涉及到父子線程之間的數(shù)據(jù)傳遞時,ThreadLocal默認的行為并不能滿足我們的需求,所以本文將介紹在SpringBoot應(yīng)用中實現(xiàn)ThreadLocal父子線程傳值的幾種方式

前言

在日常開發(fā)中,我們經(jīng)常會遇到需要在父子線程之間傳遞數(shù)據(jù)的場景。比如用戶身份信息、請求ID、鏈路追蹤ID等。

ThreadLocal作為Java中重要的線程本地變量機制,為我們提供了在單個線程內(nèi)存儲數(shù)據(jù)的便利。但是,當涉及到父子線程之間的數(shù)據(jù)傳遞時,ThreadLocal默認的行為并不能滿足我們的需求。

本文將介紹在SpringBoot應(yīng)用中實現(xiàn)ThreadLocal父子線程傳值的幾種方式。

ThreadLocal 基礎(chǔ)回顧

首先,讓我們簡單回顧一下ThreadLocal的基本原理:

public class ThreadLocal<T> {
    // 每個線程都有自己獨立的ThreadLocalMap
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
}

ThreadLocal的核心思想是為每個線程維護一個獨立的ThreadLocalMap,從而實現(xiàn)線程隔離。

問題的提出

讓我們通過一個簡單的例子來看看ThreadLocal在父子線程中的默認行為

public class ThreadLocalDemo {
    private static ThreadLocal<String> threadLocal = new ThreadLocal<>();

    public static void main(String[] args) {
        threadLocal.set("主線程的值");
        System.out.println("主線程獲取: " + threadLocal.get()); // 輸出: 主線程的值

        Thread childThread = new Thread(() -> {
            System.out.println("子線程獲取: " + threadLocal.get()); // 輸出: null
        });
        childThread.start();
    }
}

從上面的例子可以看出,子線程無法訪問父線程中設(shè)置的ThreadLocal值。這是因為每個線程都有自己獨立的ThreadLocalMap。

解決方案一:InheritableThreadLocal

Java為我們提供了InheritableThreadLocal來解決父子線程傳值的問題

public class InheritableThreadLocalDemo {
    private static InheritableThreadLocal<String> inheritableThreadLocal =
        new InheritableThreadLocal<>();

    public static void main(String[] args) {
        inheritableThreadLocal.set("主線程的值");
        System.out.println("主線程獲取: " + inheritableThreadLocal.get());

        Thread childThread = new Thread(() -> {
            System.out.println("子線程獲取: " + inheritableThreadLocal.get()); // 輸出: 主線程的值
        });
        childThread.start();
    }
}

InheritableThreadLocal 原理分析

InheritableThreadLocal的實現(xiàn)在Thread類中

public class Thread implements Runnable {
    // 父線程的inheritableThreadLocals會在創(chuàng)建子線程時復(fù)制
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        // ...
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        // ...
    }
}

InheritableThreadLocal 的局限性

  • 1. 時機限制:只在創(chuàng)建線程時進行值傳遞,后續(xù)修改不會傳遞到已創(chuàng)建的子線程
  • 2. 線程池問題:在線程池中使用時,由于線程會被復(fù)用,可能導致數(shù)據(jù)混亂

解決方案二:使用TransmittableThreadLocal

阿里巴巴開源的TransmittableThreadLocal(TTL)是解決線程池場景下父子線程傳值問題的優(yōu)秀方案:

添加依賴

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>transmittable-thread-local</artifactId>
    <version>2.14.5</version>
</dependency>

基本使用

import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.threadpool.TtlExecutors;

public class TtlDemo {
    private static TransmittableThreadLocal<String> ttl = new TransmittableThreadLocal<>();

    public static void main(String[] args) {
        ttl.set("主線程的值");
        System.out.println("主線程獲取: " + ttl.get());

        // 使用TTL裝飾的線程池
        ExecutorService executor = TtlExecutors.getTtlExecutorService(
            Executors.newFixedThreadPool(2)
        );

        executor.submit(() -> {
            System.out.println("線程池任務(wù)1獲取: " + ttl.get()); // 輸出: 主線程的值
        });

        // 修改值后提交新任務(wù)
        ttl.set("更新后的值");
        executor.submit(() -> {
            System.out.println("線程池任務(wù)2獲取: " + ttl.get()); // 輸出: 更新后的值
        });
    }
}

TTL 與Spring Boot的集成

在Spring Boot應(yīng)用中,我們可以通過以下方式集成TTL

1. 配置TTL異步執(zhí)行器

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("Async-");

        // 使用TTL裝飾
        return TtlExecutors.getTtlExecutor(executor);
    }
}

2. 使用TTL的請求攔截器

@Component
public class TtlRequestInterceptor implements HandlerInterceptor {

    private static final TransmittableThreadLocal<String> requestId =
        new TransmittableThreadLocal<>();

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                           Object handler) {
        String id = UUID.randomUUID().toString();
        requestId.set(id);
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
                              Object handler, Exception ex) {
        requestId.remove(); // 清理
    }

    public static String getRequestId() {
        return requestId.get();
    }
}

解決方案三:自定義TaskDecorator

Spring提供了TaskDecorator接口,允許我們對Runnable任務(wù)進行裝飾

@Configuration
@EnableAsync
public class CustomAsyncConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("CustomAsync-");

        // 設(shè)置自定義裝飾器
        executor.setTaskDecorator(new ContextCopyingDecorator());
        executor.initialize();

        return executor;
    }

    private static class ContextCopyingDecorator implements TaskDecorator {
        @Override
        public Runnable decorate(Runnable runnable) {
            // 獲取父線程的ThreadLocal上下文
            Map<String, Object> context = getContextFromCurrentThread();
            return () -> {
                try {
                    // 在子線程中復(fù)制上下文
                    setContextToCurrentThread(context);
                    runnable.run();
                } finally {
                    clearCurrentThreadContext();
                }
            };
        }

        private Map<String, Object> getContextFromCurrentThread() {
            Map<String, Object> context = new HashMap<>();
            // 收集當前線程的ThreadLocal值
            // 例如:從自定義的ThreadLocal中獲取
            if (UserContext.getUser() != null) {
                context.put("userInfo", UserContext.getUser());
            }
            if (RequestContextHolder.getRequestAttributes() != null) {
                context.put("requestAttributes", RequestContextHolder.getRequestAttributes());
            }
            return context;
        }

        private void setContextToCurrentThread(Map<String, Object> context) {
            // 在子線程中設(shè)置上下文
            if (context.containsKey("userInfo")) {
                UserContext.setUser((UserContext.UserInfo) context.get("userInfo"));
            }
            if (context.containsKey("requestAttributes")) {
                RequestContextHolder.setRequestAttributes((RequestAttributes) context.get("requestAttributes"));
            }
        }

        private void clearCurrentThreadContext() {
            // 清理上下文,防止內(nèi)存泄漏
            UserContext.clear();
            RequestContextHolder.resetRequestAttributes();
        }
    }
}

解決方案四:使用Spring的RequestContextHolder

在Spring Web應(yīng)用中,我們可以利用RequestContextHolder來傳遞請求上下文

@Service
public class ContextService {

    @Async
    public CompletableFuture<String> asyncMethod() {
        // 獲取父線程的請求上下文
        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();

        return CompletableFuture.supplyAsync(() -> {
            // 在異步線程中設(shè)置上下文
            RequestContextHolder.setRequestAttributes(attributes);

            try {
                // 執(zhí)行業(yè)務(wù)邏輯
                String result = doSomeWork();
                return result;
            } finally {
                RequestContextHolder.resetRequestAttributes();
            }
        });
    }

    private String doSomeWork() {
        // 獲取請求相關(guān)的信息
        HttpServletRequest request =
            ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        return "處理完成: " + request.getRequestURI();
    }
}

方案選擇建議

基于上述性能對比和適用場景,我們可以給出以下選擇建議:

  • 1. 簡單的父子線程傳值:使用InheritableThreadLocal
  • 2. 線程池場景:推薦使用TransmittableThreadLocal
  • 3. Spring異步任務(wù):使用TaskDecorator
  • 4. Web應(yīng)用請求上下文:使用RequestContextHolder

最佳實踐

1. 內(nèi)存泄漏預(yù)防

無論使用哪種方案,都要注意及時清理ThreadLocal:

public class SafeThreadLocalUsage {
    private static ThreadLocal<Object> threadLocal = new ThreadLocal<>();

    public void doWork() {
        try {
            threadLocal.set(someValue);
            // 業(yè)務(wù)邏輯
        } finally {
            threadLocal.remove(); // 防止內(nèi)存泄漏
        }
    }
}

2. 上下文封裝

建議封裝一個統(tǒng)一的上下文管理器:

public class UserContext {
    private static final ThreadLocal<UserInfo> USER_CONTEXT =
        new TransmittableThreadLocal<>();

    public static void setUser(UserInfo user) {
        USER_CONTEXT.set(user);
    }

    public static UserInfo getUser() {
        return USER_CONTEXT.get();
    }

    public static void clear() {
        USER_CONTEXT.remove();
    }

    public static class UserInfo {
        private String userId;
        private String userName;
        private String requestId;
        // getters and setters
    }
}

3. 攔截器統(tǒng)一管理

@Component
public class UserContextInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                           Object handler) {
        UserInfo userInfo = buildUserInfo(request);
        UserContext.setUser(userInfo);
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
                              Object handler, Exception ex) {
        UserContext.clear();
    }

    private UserInfo buildUserInfo(HttpServletRequest request) {
        UserInfo userInfo = new UserInfo();
        userInfo.setUserId(request.getHeader("X-User-Id"));
        userInfo.setRequestId(UUID.randomUUID().toString());
        return userInfo;
    }
}

總結(jié)

本文介紹了四種在SpringBoot中實現(xiàn)ThreadLocal父子線程傳值的方案:

  • 1. InheritableThreadLocal:最簡單的原生解決方案,適用于基礎(chǔ)場景
  • 2. TransmittableThreadLocal:功能強大的第三方解決方案,特別適合線程池場景
  • 3. TaskDecorator:Spring提供的優(yōu)雅解決方案,集成度高
  • 4. RequestContextHolder:Spring Web應(yīng)用的內(nèi)置方案

在實際項目中,我們應(yīng)該根據(jù)具體的業(yè)務(wù)場景和技術(shù)棧選擇合適的方案。

同時,要注意內(nèi)存管理和上下文清理,確保系統(tǒng)的穩(wěn)定性和性能。

以上就是SpringBoot實現(xiàn)ThreadLocal父子線程傳值的幾種方式的詳細內(nèi)容,更多關(guān)于SpringBoot ThreadLocal父子線程傳值的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • java動態(tài)綁定和靜態(tài)綁定用法實例詳解

    java動態(tài)綁定和靜態(tài)綁定用法實例詳解

    這篇文章主要介紹了java動態(tài)綁定和靜態(tài)綁定用法,結(jié)合實例形式詳細分析了java動態(tài)綁定與靜態(tài)綁定相關(guān)概念、原理、實現(xiàn)方法及使用注意事項,需要的朋友可以參考下
    2019-05-05
  • Spring boot啟動流程之解決循環(huán)依賴的方法

    Spring boot啟動流程之解決循環(huán)依賴的方法

    循環(huán)依賴,指的是兩個bean之間相互依賴,形成了一個循環(huán),spring解決循環(huán)依賴的方式是在bean的實例化完成之后,所以不要在構(gòu)造方法中引入循環(huán)依賴,因為這時對象還沒有實例化,spring也無法解決,本文給大家介紹Spring boot循環(huán)依賴的解決方法,一起看看吧
    2024-02-02
  • SpringBoot自定義MessageConverter與內(nèi)容協(xié)商管理器contentNegotiationManager詳解

    SpringBoot自定義MessageConverter與內(nèi)容協(xié)商管理器contentNegotiationManag

    這篇文章主要介紹了SpringBoot自定義MessageConverter與內(nèi)容協(xié)商管理器contentNegotiationManager的使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習吧
    2022-10-10
  • SpringMvc3+extjs4實現(xiàn)上傳與下載功能

    SpringMvc3+extjs4實現(xiàn)上傳與下載功能

    這篇文章主要為大家詳細介紹了SpringMvc3+extjs4實現(xiàn)上傳與下載功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • Java8如何使用Lambda表達式簡化代碼詳解

    Java8如何使用Lambda表達式簡化代碼詳解

    這篇文章主要給大家介紹了關(guān)于Java8如何使用Lambda表達式簡化的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-11-11
  • Java SPI用法案例詳解

    Java SPI用法案例詳解

    這篇文章主要介紹了Java SPI用法案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • Idea工具中使用Mapper對象有紅線的解決方法

    Idea工具中使用Mapper對象有紅線的解決方法

    mapper對象在service層有紅線,項目可以正常使用,想知道為什么會出現(xiàn)這種情,接下來通過本文給大家介紹下Idea工具中使用Mapper對象有紅線的問題,需要的朋友可以參考下
    2022-09-09
  • JavaWeb入門教程之分頁查詢功能的簡單實現(xiàn)

    JavaWeb入門教程之分頁查詢功能的簡單實現(xiàn)

    這篇文章主要介紹了JavaWeb入門教程之分頁查詢功能的簡單實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-11-11
  • SpringBoot異步調(diào)用相同類的解決方案

    SpringBoot異步調(diào)用相同類的解決方案

    在SpringBoot中,同一個類中調(diào)用帶有@Async注解的方法時,異步調(diào)用會失效,因為直接通過this調(diào)用方法時,并沒有通過Spring的代理對象,下面給大家分享SpringBoot異步調(diào)用相同類的解決方案,感興趣的朋友一起看看吧
    2025-02-02
  • Spring?Boot?2.x升3.x的那些事

    Spring?Boot?2.x升3.x的那些事

    最近項目需求,準備從Spring Boot 2.x升級到3.x,升級后發(fā)現(xiàn)編譯器報了一堆錯誤,本文主要介紹了Spring?Boot?2.x升3.x的那些事,具有一定的參考價值,感興趣的可以了解一下
    2024-01-01

最新評論

金湖县| 保亭| 沧州市| 井陉县| 鹤庆县| 天祝| 绥江县| 从江县| 昔阳县| 铁岭市| 怀柔区| 宁强县| 宁都县| 沾益县| 奇台县| 盐津县| 延川县| 鹤山市| 富民县| 六盘水市| 壶关县| 饶平县| 澜沧| 西和县| 江安县| 山西省| 班戈县| 遂川县| 高青县| 西峡县| 株洲县| 太白县| 诸城市| 防城港市| 山东| 大城县| 邛崃市| 林芝县| 桃园市| 闽清县| 鄂托克旗|