MST

星途 面试题库

面试题:Java中InheritableThreadLocal实现跨线程传递原理及应用

InheritableThreadLocal是Java提供的一种实现ThreadLocal跨线程传递的方式,请详细说明InheritableThreadLocal的工作原理。并举例说明在一个Web应用程序中,如何合理利用InheritableThreadLocal来解决用户登录信息在不同线程间传递的问题?
41.5万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

InheritableThreadLocal工作原理

  1. 基本概念InheritableThreadLocalThreadLocal 的子类,它允许子线程继承父线程中 InheritableThreadLocal 所设置的值。
  2. 实现机制
    • 每个 Thread 类有两个 ThreadLocal 相关的成员变量:threadLocalsinheritableThreadLocalsthreadLocals 用于存储普通 ThreadLocal 的值,而 inheritableThreadLocals 用于存储 InheritableThreadLocal 的值。
    • 当创建一个新线程时,如果父线程的 inheritableThreadLocals 不为 null,新线程会创建一个 inheritableThreadLocals 的副本。这个副本中的 InheritableThreadLocal 键值对是从父线程的 inheritableThreadLocals 复制过来的。
    • 当调用 InheritableThreadLocalget() 方法时,它会首先从当前线程的 inheritableThreadLocals 中获取值。如果不存在,则返回 null 或调用 initialValue() 方法(如果重写了该方法)。

在Web应用程序中利用InheritableThreadLocal解决用户登录信息传递问题示例

假设使用的是Spring Boot框架:

  1. 定义InheritableThreadLocal
public class UserLoginInfoHolder {
    private static final InheritableThreadLocal<UserLoginInfo> userLoginInfoThreadLocal = new InheritableThreadLocal<>();

    public static void setUserLoginInfo(UserLoginInfo userLoginInfo) {
        userLoginInfoThreadLocal.set(userLoginInfo);
    }

    public static UserLoginInfo getUserLoginInfo() {
        return userLoginInfoThreadLocal.get();
    }

    public static void removeUserLoginInfo() {
        userLoginInfoThreadLocal.remove();
    }
}

这里 UserLoginInfo 是自定义的用户登录信息类。 2. 过滤器设置用户登录信息

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@WebFilter(urlPatterns = "/*")
public class LoginInfoFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        // 假设从请求头中获取用户登录信息
        String userId = request.getHeader("user-id");
        String username = request.getHeader("username");
        UserLoginInfo userLoginInfo = new UserLoginInfo(userId, username);
        UserLoginInfoHolder.setUserLoginInfo(userLoginInfo);
        try {
            filterChain.doFilter(servletRequest, servletResponse);
        } finally {
            UserLoginInfoHolder.removeUserLoginInfo();
        }
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}
  1. 在不同线程中使用用户登录信息
import org.springframework.stereotype.Service;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Service
public class UserService {
    private final ExecutorService executorService = Executors.newFixedThreadPool(10);

    public void performTask() {
        executorService.submit(() -> {
            UserLoginInfo userLoginInfo = UserLoginInfoHolder.getUserLoginInfo();
            if (userLoginInfo != null) {
                // 可以在这里使用用户登录信息进行业务操作
                System.out.println("User ID: " + userLoginInfo.getUserId() + ", Username: " + userLoginInfo.getUsername());
            }
        });
    }
}

在上述示例中,通过过滤器将用户登录信息设置到 InheritableThreadLocal 中,后续在不同线程(如 ExecutorService 提交的任务线程)中可以获取到该用户登录信息,实现了用户登录信息在不同线程间的传递。同时,在过滤器的 finally 块中移除 InheritableThreadLocal 中的值,避免内存泄漏。