[英]Why should Java ThreadLocal variables be static
我在这里阅读了 Threadlocal 的 JavaDoc
https://docs.oracle.com/javase/1.5.0/docs/api/java/lang/ThreadLocal.html
它说“ThreadLocal 实例通常是希望将状态与线程(例如,用户 ID 或事务 ID)相关联的类中的私有静态字段。”
但我的问题是,为什么他们选择将其设为静态(通常)——“每线程”状态让事情有点混乱,但字段是静态的?
因为如果它是一个实例级别的字段,那么它实际上是“每线程 - 每实例”,而不仅仅是一个有保证的“每线程”。 这通常不是您要寻找的语义。
通常它持有一些对象,这些对象的范围限定为用户对话、Web 请求等。您不希望它们也限定为类的实例。
一个 Web 请求 => 一个 Persistence 会话。
不是一个 Web 请求 => 每个对象一个持久性会话。
要么将其设为静态,或者如果您试图避免类中的任何静态字段 - 将类本身设为单例,然后您就可以安全地使用实例级别 ThreadLocal,只要该单例在全局可用。
不必如此。 重要的是它应该是一个单例。
原因是变量是通过与线程关联的指针访问的。 它们就像具有线程作用域的全局变量,因此静态是最合适的。 这是您在 pthreads 之类的东西中获得线程本地状态的方式,所以这可能只是历史和实现的一个意外。
参考这个,这可以更好地理解。
简而言之, ThreadLocal
对象就像一个键值映射。 当线程调用ThreadLocal
get/set
方法时,它将检索/存储映射键中的线程对象,以及映射值中的值。 这就是为什么不同的线程具有不同的值副本(您要在本地存储),因为它驻留在不同的地图条目中。
这就是为什么您只需要一张地图即可保留所有值的原因。 尽管不是必需的,但您也可以使用多个映射(不声明静态)来保留每个线程对象,这完全是多余的,这就是首选静态变量的原因。
在每个线程的每个实例上使用 threadlocal 的一个用途是,如果您希望某些内容在对象的所有方法中可见并使其线程安全,而无需像对普通字段那样同步对它的访问。
static final ThreadLocal
变量是线程安全的。
static
使 ThreadLocal 变量仅可用于各个线程的多个类。 它是跨多个类的各个线程局部变量的一种全局变量声明。
我们可以使用以下代码示例检查此线程安全性。
CurrentUser
- 在 ThreadLocal 中存储当前用户 IDTestService
- 带有方法的简单服务 - 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
...
分析总结
main
执行结束并将当前用户打印为“62”,并行ForkJoinPool.commonPool-worker-1
执行结束并将当前用户打印为“87”,并行ForkJoinPool.commonPool-worker-2
执行结束并将当前用户打印为“ 31", 并行ForkJoinPool.commonPool-worker-3
执行结束并将当前用户打印为 "81"推理
并发线程能够检索正确的用户 ID,即使它已声明为“静态最终 ThreadLocal”
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.