首页 > 其他分享 >利用 ThreadLocal 打造 ContextManager 的最佳实践1

利用 ThreadLocal 打造 ContextManager 的最佳实践1

时间:2024-09-05 19:53:08浏览次数:16  
标签:product 上下文 ContextManager ThreadLocal 线程 打造 public

在实际开发中,我们经常需要维护一些上下文信息,这样可以避免在方法调用过程中传递过多的参数。例如,当 Web 服务器收到一个请求时,需要解析当前登录状态的用户,并在后续的业务处理中使用这个用户名。如果只需要维护一个上下文数据,如用户名,可以通过方法传参的方式,将用户名作为参数传递给每个业务方法。然而,如果需要维护的上下文信息较多,这种方式就显得笨拙且难以维护。

一个更加优雅的解决方案是使用 ThreadLocal 来实现请求线程的上下文管理。这样,同一线程中的所有方法都可以通过 ThreadLocal 对象直接读取和修改上下文信息,而无需在方法间传递参数。当需要维护多个上下文状态时,可以使用多个 ThreadLocal 实例来存储不同的信息。虽然这种方式在某些情况下也能接受,但在使用线程池时,问题就变得复杂了。因为线程池中的线程会被多个请求重复使用,如何将 ThreadLocal 中的上下文信息从主线程传递到线程池中的工作线程成为一个难题。

基于上述考虑,我们介绍一种基于 ThreadLocal 实现的上下文管理组件 ContextManager,它能够简化上下文信息的管理,并解决线程池环境中的上下文传递问题。

定义 ContextManager 类

首先,定义一个 ContextManager 类用于管理上下文信息。

package org.zyf.javabasic.thread.threadLocal;
 
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
 
/**
 * @program: zyfboot-javabasic
 * @description: 用于管理上下文信息
 * @author: zhangyanfeng
 * @create: 2024-06-02 13:48
 **/
public class ContextManager {
    // 静态变量,维护不同线程的上下文
    private static final ThreadLocal<ContextManager> CONTEXT_THREAD_LOCAL = new ThreadLocal<>();
 
    // 实例变量,维护每个上下文中所有的状态数据
    private final ConcurrentMap<String, Object> values = new ConcurrentHashMap<>();
 
    // 获取当前线程的上下文
    public static ContextManager getCurrentContext() {
        return CONTEXT_THREAD_LOCAL.get();
    }
 
    // 在当前上下文设置一个状态数据
    public void set(String key, Object value) {
        if (value != null) {
            values.put(key, value);
        } else {
            values.remove(key);
        }
    }
 
    // 在当前上下文读取一个状态数据
    public Object get(String key) {
        return values.get(key);
    }
 
    // 开启一个新的上下文
    public static ContextManager beginContext() {
        ContextManager context = CONTEXT_THREAD_LOCAL.get();
        if (context != null) {
            throw new IllegalStateException("A context is already started in the current thread.");
        }
        context = new ContextManager();
        CONTEXT_THREAD_LOCAL.set(context);
        return context;
    }
 
    // 关闭当前上下文
    public static void endContext() {
        CONTEXT_THREAD_LOCAL.remove();
    }
}

使用 ContextManager 进行上下文管理

假设我们有一个在线商城系统,用户在进行购物时需要进行身份认证,并且在用户进行购物操作时,需要记录用户的购物车信息。我们可以使用 ContextManager 类来管理用户的上下文信息。

package org.zyf.javabasic.thread.threadLocal;
 
import org.zyf.javabasic.skills.reflection.dto.Product;
 
/**
 * @program: zyfboot-javabasic
 * @description: 用户在进行购物时需要进行身份认证,并且在用户进行购物操作时,需要记录用户的购物车信息。
 * @author: zhangyanfeng
 * @create: 2024-06-02 14:02
 **/
public class ShoppingCartService {
    public void addToCart(Product product, int quantity) {
        // 开启一个新的上下文
        ContextManager.beginContext();
        try {
            // 将用户ID和商品信息设置到当前上下文中
            ContextManager.getCurrentContext().set("userId", getCurrentUserId());
            ContextManager.getCurrentContext().set("product", product);
            ContextManager.getCurrentContext().set("quantity", quantity);
 
            // 执行添加到购物车的逻辑
            // 这里可以调用其他方法,或者执行其他操作
            System.out.println("Adding product to cart...");
 
            checkout();
 
        } finally {
            // 关闭当前上下文
            ContextManager.endContext();
        }
    }
 
    public void checkout() {
        // 从当前上下文中读取用户ID和购物车信息
        String userId = (String) ContextManager.getCurrentContext().get("userId");
        Product product = (Product) ContextManager.getCurrentContext().get("product");
        int quantity = (int) ContextManager.getCurrentContext().get("quantity");
 
        // 执行结账逻辑
        // 这里可以根据购物车信息进行结账操作
        System.out.println("Checking out...");
        System.out.println("User ID: " + userId);
        System.out.println("Product: " + product.getName());
        System.out.println("Quantity: " + quantity);
    }
 
    private String getCurrentUserId() {
        // 模拟获取当前用户ID的方法
        return "user123";
    }
 
    public static void main(String[] args) {
        ShoppingCartService shoppingCartService = new ShoppingCartService();
        Product product = new Product();
        product.setName("iPhone");
        product.setId(1000);
 
        shoppingCartService.addToCart(product, 1);
    }
}

在这个示例中,ShoppingCartService 类模拟了一个购物车服务。在 addToCart() 方法中,我们开启了一个新的上下文,并将当前用户ID、商品信息和购买数量设置到上下文中。在 checkout() 方法中,我们从当前上下文中读取了用户ID、商品信息和购买数量,并执行了结账操作。

通过使用 ContextManager 类,我们可以轻松地在购物车服务中管理用户的上下文信息,而无需手动传递参数。

扩展 ContextManager 的使用方式

我们可以给 ContextManager 添加类似的静态方法,以简化代码的书写。当前请视业务情况进行应用和分析。

package org.zyf.javabasic.thread.threadLocal;
 
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Supplier;
 
/**
 * @program: zyfboot-javabasic
 * @description: 用于管理上下文信息
 * @author: zhangyanfeng
 * @create: 2024-06-02 13:48
 **/
public class ContextManager {
    // 其他省去
 
    // 执行带有新的上下文的任务
    public static <X extends Throwable> void runWithNewContext(Runnable task) throws X {
        beginContext();
        try {
            task.run();
        } finally {
            endContext();
        }
    }
 
    // 在新的上下文中执行任务,并返回结果
    public static <T, X extends Throwable> T supplyWithNewContext(Supplier<T> supplier) throws X {
        beginContext();
        try {
            return supplier.get();
        } finally {
            endContext();
        }
    }
}

在线程池中传递ContextManager


探讨如何基于 ThreadLocal 实现一个高效的上下文管理组件,以解决多线程环境下的数据共享和上下文管理这些问题。通过具体的代码示例和实战展示 ThreadLocal 如何为多线程编程提供一种简洁而高效的上下文管理方案。
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/xiaofeng10330111/article/details/139667074

标签:product,上下文,ContextManager,ThreadLocal,线程,打造,public
From: https://blog.csdn.net/weixin_48861542/article/details/141866312

相关文章

  • 企业级项目实战以及多年项目开发经验大牛打造 - - JVM
    JVMjvm分区Heap(堆区):主要存储new出来的对象实例,Java堆中细分为:新生代和老年代,一个新生代分为1个Eden区和2个Survivor区,说明:绝大部分对象在Eden区生成,当Eden区装填满的时候,会触发YoungGarbageCollection,即YGC。垃圾回收的时候,在Eden区实现清除策略,没有被引用的对象则直接......
  • 使用 Tailwind CSS 打造流星边框动画:提升网站视觉效果
    在现代网页设计中,良好的视觉效果不仅可以吸引用户的注意,还能提升网站的整体用户体验。今天我们要聊的就是如何使用TailwindCSS打造流星边框动画,为你的网站增添一抹炫丽的色彩。TailwindCSS是一个功能强大、灵活易用的CSS框架,帮助我们快速实现各种复杂的设计效果。而流星边......
  • AI数字实时互动新探索,打造高拟真专属AI智能体
    我们打造了一款满足想象与应用的智能体——AI实时互动。   谈谈AI智能体   当AI变得足够聪明时,用户与AI的交互将变得真实自然。于是,构建高拟真AI与用户的实时交互,已经成为企业提升数智化生产力的新思路。在这个交互过程中,存在一个极具活力的对象,就是智能体(AIAge......
  • 使用 `ThreadLocal` 管理用户会话信息的最佳实践
    在JavaWeb开发中,每个HTTP请求通常由独立的线程处理,多个线程同时执行任务时,数据的共享和隔离就变得尤为重要。为了确保每个线程能够独立地保存和访问数据,而不会与其他线程互相干扰,我们可以使用ThreadLocal。ThreadLocal为每个线程提供了一个独立的变量副本,使得每个线程都......
  • 打造自己的专属AI平台,支持本地化部署
    程序员的公众号:源1024,获取更多资料,无加密无套路!最近整理了一波电子书籍资料,包含《EffectiveJava中文版第2版》《深入JAVA虚拟机》,《重构改善既有代码设计》,《MySQL高性能-第3版》,《Java并发编程实战》等等获取方式:关注公众号并回复电子书领取,更多内容持续奉上简......
  • 如何打造IPD端到端的质量管理体系?
    作者| 杨学明话题| 产品质量     在当今竞争激烈的市场环境下,企业要想脱颖而出,必须注重产品和服务的质量,不断提升自身的竞争力,很多企业提出了“创新驱动、质量引领”的口号,产品质量的优劣往往决定了一个企业的生死存亡,低质量的产品不仅会大大增加产品的维护成本,侵......
  • HarmonyOS NEXT开发:ArkUI组件动画-打造组件定制化动效
    组件动画ArkUI为组件提供了通用的属性动画和转场动画能力的同时,还为一些组件提供了默认的动画效果。例如,List的滑动动效,Button的点击动效,是组件自带的默认动画效果。在组件默认动画效果的基础上,开发者还可以通过属性动画和转场动画对容器组件内的子组件动效进行定制。使用组......
  • 电商API接口对接:打造高效、稳定、安全的数字商业链
    在数字化浪潮的推动下,电商API接口已成为连接电商平台与商家后台系统的重要桥梁。然而,随着数据量的激增和业务需求的多样化,如何在对接过程中确保高效性、稳定性和安全性,成为了电商企业面临的重要挑战。本文将探讨有效的策略和实践,帮助企业顺利应对电商API接口对接。一、高效对......
  • Vue3打造前台+中台通用开发提效解决方案-42种前台业务模型
    Vue3打造前台+中台通用开发提效解决方案:‌42种前台业务模型引言随着前端技术的飞速发展,‌Vue.js作为主流前端框架之一,‌凭借其易用性和高性能,‌在开发领域占据了一席之地。‌特别是Vue3的发布,‌带来了CompositionAPI、‌Teleport等新特性,‌进一步提升了开发效率和用户体验。‌......
  • Java SpringBoot实战教程,手把手教你打造店铺租赁租凭平台
    ✍✍计算机毕业编程指导师**⭐⭐个人介绍:自己非常喜欢研究技术问题!专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。⛽⛽实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流!⚡⚡Java、Python、微信小程序、大数据实战项目集⚡⚡......