有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

我正在运行一个web服务,它至少接收200个RPS。基于该操作,我们使用以下代码为一些操作提供根访问权限

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()方法将根据请求返回实际调用方。问题是200个请求中有1个请求返回root,而不是实际的调用方

我注意到的一件事是不用threadlocal。remove(),我将该值设置为null。预计getRunner()rootContext.get() != null条件将失败并返回实际调用方

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

谢谢你的帮助


共 (1) 个答案

  1. # 1 楼答案

    你的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的引用,这反过来可能会阻止类卸载

    如果调用rootContext.remove();,则rootContext将从该映射中删除