Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ruby-on-rails/65.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么Java线程局部变量应该是静态的_Java_Multithreading - Fatal编程技术网

为什么Java线程局部变量应该是静态的

为什么Java线程局部变量应该是静态的,java,multithreading,Java,Multithreading,我在这里读Threadlocal的JavaDoc 上面写着 ThreadLocal实例通常是希望将状态与线程(例如,用户ID或事务ID)关联的类中的私有静态字段 但我的问题是,他们为什么选择将其设置为静态(通常)-设置“每线程”状态会让人有点困惑,但字段是静态的?因为如果它是实例级字段,那么它实际上将是“每线程-每实例”,而不仅仅是保证的“每线程”这通常不是你想要的语义 通常,它包含一些对象,这些对象的作用域是用户对话、Web请求等。您不希望它们也被划分为类实例的子作用域。 一个web请求=>

我在这里读Threadlocal的JavaDoc

上面写着 ThreadLocal实例通常是希望将状态与线程(例如,用户ID或事务ID)关联的类中的私有静态字段


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

因为如果它是实例级字段,那么它实际上将是“每线程-每实例”,而不仅仅是保证的“每线程”这通常不是你想要的语义

通常,它包含一些对象,这些对象的作用域是用户对话、Web请求等。您不希望它们也被划分为类实例的子作用域。
一个web请求=>一个持久性会话。

不是一个web请求=>每个对象一个持久性会话。

原因是变量是通过与线程关联的指针访问的。它们的行为类似于具有线程范围的全局变量,因此static是最合适的。这是在pthreads中获取线程本地状态的方法,因此这可能只是历史和实现的一个意外情况。

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

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

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

请参阅,这样可以更好地理解

简而言之,
ThreadLocal
对象的工作方式类似于键值映射。当线程调用
ThreadLocal
get/set
方法时,它将检索/存储映射键中的线程对象,以及映射值中的值。这就是为什么不同的线程具有不同的复制值(您希望本地存储),因为它位于不同的映射条目中


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

静态最终ThreadLocal
变量是线程安全的

static
使ThreadLocal变量仅可用于各个线程的多个类。这是一种全局变量,它是各个线程局部变量在多个类之间的衰减

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

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

分析总结

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


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

    我喜欢这个解释,因为它显示了如何使用ThreadLocal。每个实例的线程都可以是一个有用的语义,但该模式的大多数用法都会涉及太多对象,因此最好使用
    ThreadLocal
    来保存对散列集的引用,该散列集将对象映射到每个线程实例。@可选,它只是意味着非静态
    ThreadLocal
    的每个实例都会保存自己的线程本地数据,即使这些数据是
    ThreadLocal
    实例存在于同一线程中。这样做并不一定是错误的——我想这可能是两种模式中最不受欢迎的一种
    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());
      });
    
    }
    
    }
    
    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
    ...