什么时候 ThreadLocal 优于局部变量?

When is ThreadLocal preferred over Local Variables?

ThreadLocal 在 Java 中说:

The ThreadLocal class in Java enables you to create variables that can only be read and written by the same thread. Thus, even if two threads are executing the same code, and the code has a reference to a ThreadLocal variable, then the two threads cannot see each other's ThreadLocal variables.

我的问题是:当我们需要获取一个线程特定的变量时,我们不能只在一个方法中将该变量声明为局部变量吗?因为每个线程都有自己的堆栈,因此它有自己的变量副本。我在这里遗漏了什么吗?

ThreadLocal could be best choice in scenarios when state needs to be associated with thread e.g. for global variables (if semantic permits) because ThreadLocal keeps values of variables confined to a thread; so when a thread T runs get on it, thread T gets the value which was set by itself not by any other threads.

来自 this 篇文章。

当变量在线程的 class 自身内部并且作用域对每个线程来说都是本地时,可以使用局部变量。与此相反,当变量在本地范围之外并作为共享代码的一部分存在并且语义允许每个线程保留此变量的副本而不是所有线程的单个副本时,则使用 ThreadLocal。

ThreadLocal 而不是 局部变量的替代方法。您使用 ThreadLocal 表示必须 static 但不能在线程之间共享的数据。

static final ThreadLocal<MyFoo> myFoo =
    ThreadLocal.withInitial(() -> new MyFoo());

如果您有一个 ThreadLocal 变量而不是 static,那么您要么在做一些过于复杂的事情,要么在做一些完全错误的事情。

另一方面,如果您有任何变量 static(无论是否为 ThreadLocal),那么您应该意识到这是一种会限制您能力的设计选择测试和发展程序。

根据 oracle 文档,

ThreadLocal 用于不同的目的。

看看这个class的意图:

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).

下面的代码块生成每个线程本地的唯一标识符。线程的 ID 在它第一次调用 ThreadId.get() 时分配,并在后续调用中保持不变。

 import java.util.concurrent.atomic.AtomicInteger;

 public class ThreadId {
     // Atomic integer containing the next thread ID to be assigned
     private static final AtomicInteger nextId = new AtomicInteger(0);

     // Thread local variable containing each thread's ID
     private static final ThreadLocal<Integer> threadId =
         new ThreadLocal<Integer>() {
             @Override protected Integer initialValue() {
                 return nextId.getAndIncrement();
         }
     };

     // Returns the current thread's unique ID, assigning it if necessary
     public static int get() {
         return threadId.get();
     }
 }

回到您的查询:

When we need to get a variable specific to a thread, can't we just declare that variable as a local variable inside a method? Because every thread has its own stack and thus it gets its own copy of variables. Am I missing something here?

是的。你在这里遗漏了一些东西。

在方法内部声明的变量的范围以方法生命周期结束。

在 ThreadLocal 变量的情况下,只要线程处于活动状态并且 ThreadLocal 实例可访问,每个线程都会持有对其线程局部变量副本的隐式引用。您可以在其生命周期中多次重新进入线程,但您仍然可以保留变量。