面试题答案
一键面试InheritableThreadLocal工作原理
- 基本概念:
InheritableThreadLocal
是ThreadLocal
的子类,它允许子线程继承父线程中InheritableThreadLocal
所设置的值。 - 实现机制:
- 每个
Thread
类有两个ThreadLocal
相关的成员变量:threadLocals
和inheritableThreadLocals
。threadLocals
用于存储普通ThreadLocal
的值,而inheritableThreadLocals
用于存储InheritableThreadLocal
的值。 - 当创建一个新线程时,如果父线程的
inheritableThreadLocals
不为null
,新线程会创建一个inheritableThreadLocals
的副本。这个副本中的InheritableThreadLocal
键值对是从父线程的inheritableThreadLocals
复制过来的。 - 当调用
InheritableThreadLocal
的get()
方法时,它会首先从当前线程的inheritableThreadLocals
中获取值。如果不存在,则返回null
或调用initialValue()
方法(如果重写了该方法)。
- 每个
在Web应用程序中利用InheritableThreadLocal解决用户登录信息传递问题示例
假设使用的是Spring Boot框架:
- 定义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();
}
}
- 在不同线程中使用用户登录信息:
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
中的值,避免内存泄漏。