繁体   English   中英

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

[英]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 中存储当前用户 ID
  • 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. “主”线程启动并将当前用户设置为“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"

推理

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM