首页 > 解决方案 > 关于 Java 中的 Threadlocal 的安全性

问题描述

目前,我有一个网络项目。我在 SpringMVC 的拦截器中的 threadlocal 中保存了一个类似 sessionid 的变量,并在 postHandle 方法中将其删除。但我想知道这是否安全。前任。如果一个线程保存了一个 sessionid,那么 CPU 上下文切换发生了,在这种情况下,其他东西会占用这个线程并设置另一个 sessionid 或在 postHandle 中删除它。当我们切换回来时, sessionid 发生了变化。如果这是可能的,我们还有其他解决方案吗?

@Interceptor
public class BusinessInterceptor extends HandlerInterceptorAdapter {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
    Object handler) throws Exception {
    ThreadLocalUtil.contextThreadLocal.set(createSessionId());
    return true;
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
    Object handler, Exception ex)
    throws Exception {
    ThreadLocalUtil.contextThreadLocal.remove();
}
}

标签: javaspringmultithreadingspring-mvcthread-safety

解决方案


您对Context Switch的含义感到有些困惑。仅从维基百科看,它被定义为:

在多任务上下文中,它指的是存储一个任务的系统状态的过程,以便可以暂停任务并恢复另一个任务。

存储一个任务的系统状态意味着存储一个线程可以访问的所有值,这当然包括线程局部变量。因此,上下文切换是关于共享 CPU 时间,而不是关于跨线程共享数据。(嗯,是的,当操作系统从用户模式转移到内核模式时,还有上下文切换,但我想这不是 OP 在他心中的一种)。

此外,线程局部变量将根据有权访问它的线程持有不同的值。如果我们看一下ThreadLocalJava 中的定义:

这些变量不同于它们的正常对应变量,因为每个访问一个(通过它的getset方法)的线程都有它自己的、独立初始化的变量副本。

所以,答案是:不,没有其他线程能够读取该线程局部变量的值,这与正在发生的上下文切换的数量无关。


在另一个说明中,我会担心您在代码中所做的假设。我的意思是,在每个请求的线程本地存储一个值让我认为您假设每个请求执行模型有一个线程。此外,依靠使用线程局部变量来传递值与使用全局变量是等价的,但不同的是,它们更像是部分全局变量,并且通常在运行时带来有趣的惊喜。

您必须意识到,在这个我们拥有异步/响应式执行模型、HTTP/2 等的时代,您的假设将落空,因为您将有多个线程执行给定的请求......并且因为也就是说,当任何其他线程尝试访问它时,您的线程局部变量要么未初始化,要么持有完全意外的值。这会带来很多麻烦。

我建议您找到一种不同的方式来传递您的会话 ID,并且我建议您明确地将其作为可能需要它的后续函数的参数。


推荐阅读