未为 java 中的下一个线程清除 ThreadLocal 值

ThreadLocal value not cleared for the next thread in java

我是 运行 至少接收 200 RPS 的 Web 服务。根据操作,我们为少数操作提供 root 访问权限,并使用以下代码。

private static final ThreadLocal<String> rootContext = new ThreadLocal<String>();

public Optional<String> getRunner() {
    if (rootContext.get() != null) {
        return rootContext.get();
    } else {
        return getCurrentRunner();
    }
}

public void rootAccess(Runnable runnable) {
    rootContext.set("root");
    runnable.run();
    rootContext.set(null);
}

getCurrentRunner() 方法将return 实际调用者基于请求。问题是 200 个请求中有 1 个请求 returns root 而不是实际调用者。

我注意到的一件事是我没有使用 threadlocal.remove(),而是将该值设置为 null。预计 getRunner() rootContext.get() != null 条件将失败并且 return 实际调用者。

如何解决这个问题?设置 rootContext.remove() 会解决这个问题吗?如果是,如何?

感谢您的帮助

您的 rootAccess 方法有两个问题:

  1. 如果 Runnable 抛出 RuntimeException,则不会删除 ThreadLocal(可能是您所看到的)
  2. rootContext.set(null); 仍然保留与 运行 线程关联的 ThreadLocal 实例,最好做 rootContext.remove();

更正这两点就是把rootAccess()改成

public void rootAccess(Runnable runnable) {
    rootContext.set("root");
    try {
        runnable.run();
    } finally {
        rootContext.remove();
    }
}

为什么 rootContext.set(null); 通常是个问题?

每个线程基本上都有一个类似于 Map<ThreadLocal, ?> 的数据结构,其中键是您的 ThreadLocal 实例 (rootContext),值是您通过它关联的值rootContext.set(xx);

如果您调用 rootContext.set(null);,那么 rootContext 仍在该映射中,因此执行此行的每个线程(来自线程池,这意味着线程很长 运行)保留对 rootContext 的引用可能会阻止 class 卸载。

如果您调用 rootContext.remove();rootContext 将从该地图中删除。