将ThreadLocal与ExecutorService一起使用是危险的吗?

Hit*_*esh 8 java multithreading thread-local

我在下面的博客上看到了ThreadLocals的概念:

https://www.baeldung.com/java-threadlocal

它说"不要将ThreadLocal与ExecutorService一起使用"

它说明了下面使用ThreadLocals的示例.

public class ThreadLocalWithUserContext implements Runnable {

    private static ThreadLocal<Context> userContext 
      = new ThreadLocal<>();
    private Integer userId;
    private UserRepository userRepository = new UserRepository();

    @Override
    public void run() {
        String userName = userRepository.getUserNameForUserId(userId);
        userContext.set(new Context(userName));
        System.out.println("thread context for given userId: "
          + userId + " is: " + userContext.get());
    }

    // standard constructor
}
Run Code Online (Sandbox Code Playgroud)

在帖子的最后,它提到:

"如果我们想使用ExecutorService并向其提交Runnable,那么使用ThreadLocal将产生非确定性结果 - 因为我们无法保证每次给定userId的每个Runnable操作都将由同一个线程处理执行.

因此,我们的ThreadLocal将在不同的userId之间共享.这就是为什么我们不应该将TheadLocal与ExecutorService一起使用.只有当我们完全控制哪个线程将选择要执行哪个可运行动作时,才应该使用它."

这个解释对我来说是一个保镖.我特意在网上做了一些研究,但是我得不到多少帮助,有些专家请详细说明上述解释吗?它是作者观点还是真正的威胁?

Gho*_*ica 7

将 ThreadLocal 视为由同一线程执行的代码的某种“内存缓存”。完全相同的线程。在不同线程上执行的代码之间共享 ThreadLocal 是一个坏主意。

Tha javadoc 明确指出:

此类提供线程局部变量。这些变量不同于它们的普通对应变量,因为每个访问一个(通过其 get 或 set 方法)的线程都有自己的、独立初始化的变量副本。ThreadLocal 实例通常是希望将状态与线程相关联的类中的私有静态字段(例如,用户 ID 或事务 ID)。

换句话说:使用 ThreadLocals的目标是为在不同线程中运行的“每个”代码提供“线程特定”数据。

另一方面,ExecutorService 首先是一个接口:您根本不知道它是由单个线程提供支持,还是(更有可能)由多个线程提供支持。

换句话说:使用 ExecutorService 会很快导致多个不同的线程运行您的 Runnables/Tasks。然后您将在这些多个线程之间共享您的 ThreadLocal。

因此,“危险”可能是错误的词。使用 ThreadLocal的目标是拥有每个线程的存储,而 ExecutorService 是关于由未知数量的线程执行的代码。这两件事根本不能很好地结合在一起。

重点不同:一个概念强调与非常具体的“活动”相关的长期存在的线索。另一个概念是关于使用未知数量的无名线程执行小的、独立的活动。

  • 抱歉,我听不懂?我们怎么能说“ThreadLocal”将在池中的线​​程之间共享,因为池中的每个线程都能够保存自己的 ThreadLocal 个人副本? (3认同)

Bas*_*que 5

需要注意的是,您的多次Runnable运行可能会在不同的线程上执行。执行程序服务可以由单个线程支持,但也可以由线程池支持。在您的后续执行中Runnable,不同的线程将访问不同的ThreadLocal.

因此,您当然可以ThreadLocal在一次运行中使用Runnable. 但它不太可能有用,因为通常 a 的目的ThreadLocal是暂时保留一个值。相反,aRunnable通常应该是短暂的。

ThreadLocal所以,不,通常将 a与线程池一起使用是没有意义的。