在实际开发中,我们经常需要维护一些上下文信息,这样可以避免在方法调用过程中传递过多的参数。例如,当 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