为什么 Java ThreadLocal 变量应该是静态的

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2784009/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 12:49:48  来源:igfitidea点击:

Why should Java ThreadLocal variables be static

javamultithreading

提问by kellyfj

I was reading the JavaDoc for Threadlocal here

我在这里阅读了 Threadlocal 的 JavaDoc

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

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

and it says "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). "

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

But my question is why did they choose to make it static (typically) - it makes things a bit confusing to have "per thread" state but the fields are static?

但我的问题是,为什么他们选择将其设为静态(通常)——“每线程”状态让事情有点混乱,但字段是静态的?

采纳答案by Affe

Because if it were an instance level field, then it would actually be "Per Thread - Per Instance", not just a guaranteed "Per Thread." That isn't normally the semantic you're looking for.

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

Usually it's holding something like objects that are scoped to a User Conversation, Web Request, etc. You don't want them also sub-scoped to the instance of the class.
One web request => one Persistence session.
Not one web request => one persistence session per object.

通常它持有一些对象,这些对象的范围限定为用户对话、Web 请求等。您不希望它们也限定为类的实例。
一个 Web 请求 => 一个 Persistence 会话。
不是一个 Web 请求 => 每个对象一个持久性会话。

回答by Ukko

The reason is that the variables are accessed via a pointer associated with the thread. They act like global variables with thread scope, hence static is the closest fit. This is the way that you get thread local state in things like pthreads so this might just be an accident of history and implementation.

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

回答by irreputable

It doesn't have to be. The important thing is that it should be a singleton.

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

回答by Adnan Memon

Either make it static or if you are trying to avoid any static fields in your class - make the class itself a singleton and then you can safely use the an instance level ThreadLocal as long as you have that singleton available globally.

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

回答by Chris Mawata

A use for an threadlocal on a per instance per thread is if you want something to be visible in all methods of an object and have it thread safe without synchronizing access to it like you would so for an ordinary field.

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

回答by GMsoF

Refer to this, this give better understanding.

参考这个,这可以更好地理解。

In short, ThreadLocalobject work like a key-value map. When the thread invoke ThreadLocalget/setmethod, it will retrieve/store the thread object in the map's key, and the value in the map's value. That's why different threads has different copied of value (that you want to store locally), because it resides in different map's entry.

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

That's why you only need one map to keep all values. Although not necessary, you can have multiple map (without declare static) to keep each thread object as well, which, it is totally redundant, that's why static variable is preferred.

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

回答by shijin raj

static final ThreadLocalvariables are thread safe.

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

staticmakes the ThreadLocal variable available across multiple classes for the respective thread only. it's a kind of Global variable decaration of the respective thread local variables across multiple classes.

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

We can check the this thread safety with the following code sample.

我们可以使用以下代码示例检查此线程安全性。

  • CurrentUser- stores the current user id in ThreadLocal
  • TestService- Simple service with method - getUser()to fetch the current user from CurrentUser.
  • TestThread- this class used for creating multiple threads and set userids concurrently
  • 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());
  });

}

}

.

.

Run TestThread main class. Output -

运行 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
...

Analysis summary

分析总结

  1. "main" thread starts and set current user as "62", parallely "ForkJoinPool.commonPool-worker-2" thread starts and set current user as "31", parallely "ForkJoinPool.commonPool-worker-3" thread starts and set current user as "81", parallely "ForkJoinPool.commonPool-worker-1" thread starts and set current user as "87" 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
  2. All these above threads will sleep for 3 seconds
  3. mainexecution ends and print the current user as "62", parallely ForkJoinPool.commonPool-worker-1execution ends and print the current user as "87", parallely ForkJoinPool.commonPool-worker-2execution ends and print the current user as "31", parallely ForkJoinPool.commonPool-worker-3execution ends and print the current user as "81"
  1. “主”线程启动并将当前用户设置为“62”,并行“ForkJoinPool.commonPool-worker-2”线程启动并将当前用户设置为“31”,并行“ForkJoinPool.commonPool-worker-3”线程启动并设置当前用户为“81”,并行“ForkJoinPool.commonPool-worker-1”线程启动并将当前用户设置为“87” 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
  2. 以上所有线程将休眠 3 秒
  3. main执行结束并将当前用户打印为“62”,并行ForkJoinPool.commonPool-worker-1执行结束并将当前用户打印为“87”,并行ForkJoinPool.commonPool-worker-2执行结束并将当前用户打印为“31”,并行ForkJoinPool.commonPool-worker-3执行结束并将当前用户打印为“81”

Inference

推理

Concurrent Threads are able to retrieve correct userids even if it has declared as "static final ThreadLocal"

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