首页 > 其他分享 >如何用 ThreadLocal 构建强大的 ContextManager

如何用 ThreadLocal 构建强大的 ContextManager

时间:2024-09-05 19:54:24浏览次数:3  
标签: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/141818499

相关文章

  • 轻松管理上下文:ThreadLocal 助力 ContextManager
    在实际开发中,我们经常需要维护一些上下文信息,这样可以避免在方法调用过程中传递过多的参数。例如,当Web服务器收到一个请求时,需要解析当前登录状态的用户,并在后续的业务处理中使用这个用户名。如果只需要维护一个上下文数据,如用户名,可以通过方法传参的方式,将用户名作为参数传......
  • 利用 ThreadLocal 打造 ContextManager 的最佳实践1
    在实际开发中,我们经常需要维护一些上下文信息,这样可以避免在方法调用过程中传递过多的参数。例如,当Web服务器收到一个请求时,需要解析当前登录状态的用户,并在后续的业务处理中使用这个用户名。如果只需要维护一个上下文数据,如用户名,可以通过方法传参的方式,将用户名作为参数传......
  • 深入了解Vite:依赖预构建原理
    前言前面我们有提到Vite在开发阶段,提倡的是一个no-bundle的理念,不必与webpack那样需要先将整个项目进行打包构建。但是no-bundle的理念只适合源代码部分(我们自己写的代码),vite会将项目中的所有模块分为依赖与源码两部分。依赖:指的是一些不会变动的一些模块,如:node_modules中的第......
  • libreoffice24.2--arm环境源码编译--构建RPM包
    1、下载源代码地址:https://zh-cn.libreoffice.org/download/libreoffice/ 2、构建环境,使用docker容器,在容器内编译,使用的基础镜像是:fedora:latest,使用过centos9和centos10,centos9gcc版本不够;centos10安装fakeroot包有问题,无法构建rpm包。3、在解压后的libreoffice-24.2.......
  • 基于阿里云函数计算(FC)x 云原生 API 网关构建生产级别 LLM Chat 应用方案最佳实践
    作者:计缘LLMChat应用大家应该都不陌生,这类应用也逐渐称为了我们日常的得力助手,如果只是个人使用,那么目前市面上有很多方案可以快速的构建出一个LLMChat应用,但是如果要用在企业生产级别的项目中,那对整体部署架构,使用组件的性能,健壮性,扩展性要求还是比较高的。本文带大家了解一......
  • 构建高效医护人员排班系统:Spring Boot框架的优势
    1系统概述1.1研究背景随着计算机技术的发展以及计算机网络的逐渐普及,互联网成为人们查找信息的重要场所,二十一世纪是信息的时代,所以信息的管理显得特别重要。因此,使用计算机来管理医护人员排班系统的相关信息成为必然。开发合适的医护人员排班系统,可以方便管理人员对医护......
  • 浅述GIS技术与EasyCVR的深度融合:构建视频监控的地理空间信息化综管平台
    随着科技的飞速发展,视频监控与地理信息系统(GIS)技术的融合在各行各业中展现出强大的应用潜力和价值。旭帆科技EasyCVR视频平台,作为一款高性能的视频汇聚管理平台,凭借其强大的视频处理、汇聚与融合能力,结合GIS技术,为智慧安防、应急救援等领域提供了更为全面、高效的解决方案。GIS技......
  • 视频监控系统布局策略:EasyCVR视频汇聚平台构建高效、全面的安全防线
    随着科技的飞速发展,视频监控系统已成为现代社会安全防范的重要组成部分,广泛应用于公共场所、企业园区、住宅小区等各个领域。一个科学合理的视频监控系统布局与选型策略,不仅能够显著提升安全监控的效率和效果,还能在关键时刻提供关键证据,保障人员与财产的安全。一、需求分析:明确监......
  • 用 React 构建一个简单的计算器:前端开发实战演练
    在前端开发中,React是一个非常流行的JaScript框架,广泛用于构建用户界面。今天我们将通过一个实战项目,展示如何用React构建一个简单的计算器,这不仅可以帮助你巩固对React的基础知识,还能让你对实际开发有更深的理解。一、项目搭建你需要确保本地环境已经安装了Node.js和n......
  • 超强台风“摩羯”来临:EasyCVR平台如何汇聚城市视频资源,构建应急监测网
    一、背景概述2024年第11号台风“摩羯”自生成以来,迅速加强为超强台风级别,预计将在海南琼海到广东电白一带沿海登陆,带来16-17级的强风和巨浪。中央气象台和各地气象部门纷纷发布预警,各级政府和相关部门紧急启动应急响应机制,全力做好防台抗灾工作。我国作为自然灾害多发的国家,每年......