这个 LocalThread 会覆盖其他 Servlet 请求的数据吗?

Could This LocalThread Overwrite Other Servlet Requests' Data?

我有以下请求上下文 class 用于收集某些信息以供将来在 Servlet 过滤器中处理:

public class ApplicationRequestContext {
  private static final ThreadLocal<AppRequest> tl = new ThreadLocal<AppRequest>();

  public static void begin(HttpServletRequest httpRequest) {
    tl.set(getAppRequest(httpRequest));
  }

  public static void finish() {
    tl.remove();
  }
}

getAppRequest(httpRequest) 只是 returns 一个对象。在 Servlet 过滤器中...

public class ApplicationServletFilter implements javax.servlet.Filter {
  @Override

  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    try {
      ApplicationRequestContext.begin(httpRequest);
      /* other stuff */
      chain.doFilter(httpRequest, httpResponse);
    } catch (Exception e) { /* … */ }
    finally {
      ApplicationRequestContext.finish();
    }
  }
}

我想知道,此设置是否可以有多个传入请求 可能 覆盖彼此的数据(getAppRequest(httpRequest) 对象显然是唯一的)?这甚至是线程安全的吗?

是的,是 thread-safe。而且,它不能覆盖彼此的数据。

因为,

This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).

https://docs.oracle.com/javase/8/docs/api/java/lang/ThreadLocal.html

您必须记住,每个 Thread 都有自己的 ThreadLocal —— 实际上,准确地说,是自己的 ThreadLocal.ThreadLocalMap(我使用 OpenJDK 作为示例)。

当您的名为 tlThreadLocal 调用其 set() method, it basically creates a new ThreadLocalMap and have tl as a reference as a key to create a ThreadLocalMap.Entry

由于每个 Servlet 请求都使用一个唯一的线程(来自托管线程池或生成一个新线程,并且没有两个 Servlet 线程处理相同的请求),因此 tl.set() ThreadLocal 的新创建 ThreadLocalMap 作为对该 Servlet ThreadThreadLocal.ThreadLocalMap.

的引用

现在,当 tl.get() is called, we simply get the Servlet Thread's ThreadLocalMap, and do a lookup with ThreadLocalMap.getEntry() 时,获取我们输入的值,例如getAppRequest(httpRequest).

这里要记住的要点是,是的,您正在使用一个名为 tlThreadLocal 对象,但每个 Thread,在这种情况下,一个线程用于处理每个请求的 Servlet 容器,包括其过滤器,将拥有自己的 ThreadLocal.ThreadLocalMap,并将此 ThreadLocal 对象的 [weak] 引用作为其键。

因此,t1 只是 ThreadLocal.ThreadLocalMap.Entry (tl.ThreadLocalMap.Entry) 的 table 中的一个键,用于获取 ThreadLocalThread.ThreadLocalMap这是该线程独有的。因此,它是 thread-safe,那些 begin()finish() 调用基本上是在每个 Thread 中放入数据。