【问题标题】:Why should Java ThreadLocal variables be static为什么 Java ThreadLocal 变量应该是静态的
【发布时间】:2011-02-16 13:27:34
【问题描述】:

我在这里阅读 Threadlocal 的 JavaDoc

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

它说 “ThreadLocal 实例通常是类中的私有静态字段,它们希望将状态与线程相关联(例如,用户 ID 或事务 ID)。”

但我的问题是,为什么他们选择将其设为静态(通常) - 让“每个线程”状态但字段是静态的会让人有些困惑?

【问题讨论】:

    标签: java multithreading


    【解决方案1】:

    因为如果它是一个实例级字段,那么它实际上将是“每线程 - 每实例”,而不仅仅是一个有保证的“每线程”。这通常不是您要寻找的语义。

    通常它包含一些对象,例如用户对话、Web 请求等范围内的对象。您不希望它们也属于类实例的子范围。
    一个 Web 请求 => 一个持久性会话。
    不是一个 Web 请求 => 每个对象一个持久会话。

    【讨论】:

    • 我喜欢这个解释,因为它展示了如何使用 ThreadLocal
    • Per-thread-per-instance 可能是一种有用的语义,但该模式的大多数用途将涉及如此多的对象,因此最好使用 ThreadLocal 来保存对哈希的引用- 将对象映射到每个线程实例的集合。
    • @optional 这只是意味着非静态ThreadLocal 的每个实例都将保存它自己的线程本地数据,即使这些ThreadLocal 实例存在于同一个线程中。这样做不一定是错的——我想这可能是两者中最不受欢迎的模式
    【解决方案2】:

    要么将其设为静态,要么如果您试图避免类中的任何静态字段 - 将类本身设为单例,然后您可以安全地使用实例级别的 ThreadLocal,只要该单例在全局范围内可用。

    【讨论】:

      【解决方案3】:

      不必如此。重要的是它应该是一个单例。

      【讨论】:

        【解决方案4】:

        原因是变量是通过与线程关联的指针访问的。它们就像具有线程范围的全局变量一样,因此静态是最合适的。这是您在 pthread 之类的东西中获取线程本地状态的方式,因此这可能只是历史和实现的意外。

        【讨论】:

          【解决方案5】:

          参考this,这样更好理解。

          简而言之,ThreadLocal 对象就像键值映射一样工作。当线程调用ThreadLocalget/set 方法时,它将检索/存储映射键中的线程对象,以及映射值中的值。这就是为什么不同的线程有不同的值副本(你想在本地存储),因为它驻留在不同的映射条目中。

          这就是为什么您只需要一张地图来保存所有值。虽然不是必需的,但您也可以使用多个映射(无需声明静态)来保留每个线程对象,这完全是多余的,这就是首选静态变量的原因。

          【讨论】:

            【解决方案6】:

            在每个线程的每个实例上使用 threadlocal 是,如果您希望某些内容在对象的所有方法中可见,并使其线程安全,而无需像普通字段那样同步访问它。

            【讨论】:

              【解决方案7】:

              static final ThreadLocal 变量是线程安全的。

              static 使 ThreadLocal 变量仅适用于各个线程的多个类。它是跨多个类的各个线程局部变量的一种全局变量声明。

              我们可以用下面的代码示例检查这个线程的安全性。

              • CurrentUser - 将当前用户 id 存储在 ThreadLocal 中
              • TestService - 带有方法的简单服务 - getUser() 从 CurrentUser 获取当前用户。
              • TestThread - 这个类用于创建多个线程并同时设置用户标识

              .

              public class CurrentUser
              
              public class CurrentUser {
              private static final ThreadLocal<String> CURRENT = new ThreadLocal<String>();
              
              public static ThreadLocal<String> getCurrent() {
                  return CURRENT;
              }
              
              public static void setCurrent(String user) {
                  CURRENT.set(user);
              }
              
              }
              
              public class TestService {
              
              public String getUser() {
                  return CurrentUser.getCurrent().get();
              }
              
              }
              

              .

              import java.util.ArrayList;
              import java.util.List;
              
              public class TestThread {
              
              public static void main(String[] args) {
              
                List<Integer> integerList = new ArrayList<>();
              
                //creates a List of 100 integers
                for (int i = 0; i < 100; i++) {
              
                  integerList.add(i);
                }
              
                //parallel stream to test concurrent thread execution
                integerList.parallelStream().forEach(intValue -> {
              
                  //All concurrent thread will set the user as "intValue"
                  CurrentUser.setCurrent("" + intValue);
                  //Thread creates a sample instance for TestService class
                  TestService testService = new TestService();
                  //Print the respective thread name along with "intValue" value and current user. 
                  System.out.println("Start-"+Thread.currentThread().getName()+"->"+intValue + "->" + testService.getUser());
              
                  try {
                    //all concurrent thread will wait for 3 seconds
                    Thread.sleep(3000l);
                  } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                  }
              
                  //Print the respective thread name along with "intValue" value and current user.
                  System.out.println("End-"+Thread.currentThread().getName()+"->"+intValue + "->" + testService.getUser());
                });
              
              }
              
              }
              

              .

              运行 TestThread 主类。 输出 -

              Start-main->62->62
              Start-ForkJoinPool.commonPool-worker-2->31->31
              Start-ForkJoinPool.commonPool-worker-3->81->81
              Start-ForkJoinPool.commonPool-worker-1->87->87
              End-main->62->62
              End-ForkJoinPool.commonPool-worker-1->87->87
              End-ForkJoinPool.commonPool-worker-2->31->31
              End-ForkJoinPool.commonPool-worker-3->81->81
              Start-ForkJoinPool.commonPool-worker-2->32->32
              Start-ForkJoinPool.commonPool-worker-3->82->82
              Start-ForkJoinPool.commonPool-worker-1->88->88
              Start-main->63->63
              End-ForkJoinPool.commonPool-worker-1->88->88
              End-main->63->63
              ...
              

              分析总结

              1. “main”线程启动并将当前用户设置为“62”,并行“ForkJoinPool.commonPool-worker-2”线程启动并将当前用户设置为“31”,并行“ForkJoinPool.commonPool-worker-3”线程启动并将当前用户设置为“81”,并行“ForkJoinPool.commonPool-worker-1”线程启动并将当前用户设置为“87” 开始-主->62->62 Start-ForkJoinPool.commonPool-worker-2->31->31 Start-ForkJoinPool.commonPool-worker-3->81->81 Start-ForkJoinPool.commonPool-worker-1->87->87
              2. 以上所有线程都会休眠 3 秒
              3. main 执行结束并将当前用户打印为“62”,并行ForkJoinPool.commonPool-worker-1 执行结束并将当前用户打印为“87”,并行ForkJoinPool.commonPool-worker-2 执行结束并将当前用户打印为“31”, 并行ForkJoinPool.commonPool-worker-3 执行结束并将当前用户打印为“81”

              推理

              并发线程能够检索正确的用户 ID,即使它已声明为“静态最终线程本地”

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2013-04-21
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2012-04-11
                相关资源
                最近更新 更多