为什么局部变量在Java中是安全的

时间:2012-10-10 18:22:06

标签: java thread-safety

我正在阅读Java中的多线程,我遇到了这个

  

本地变量在Java中是线程安全的。

从那时起,我一直在思考如何/为什么局部变量是线程安全的。

请有人告诉我。

9 个答案:

答案 0 :(得分:93)

创建线程时,它将创建自己的堆栈。两个线程将有两个堆栈,一个线程永远不会与其他线程共享其堆栈。

程序中定义的所有局部变量都将在堆栈中分配内存(如Jatin所述,这里的内存意味着对象的引用值和基本类型的值)(线程的每个方法调用都会自己创建一个堆栈帧堆)。一旦该线程完成方法执行,就会删除堆栈帧。

Stanford professor in youtube有很棒的讲座可以帮助你理解这个概念。

答案 1 :(得分:17)

局部变量存储在每个线程自己的堆栈中。这意味着线程之间永远不会共享局部变量。这也意味着所有本地原始变量都是线程安全的。

public void someMethod(){

   long threadSafeInt = 0;

   threadSafeInt++;
}

对象的本地引用略有不同。引用本身不共享。但是,引用的对象不存储在每个线程的本地堆栈中。所有对象都存储在共享堆中。如果本地创建的对象永远不会转义它创建的方法,那么它是线程安全的。实际上,只要这些方法或对象都不会使传递的对象可用于其他线程,您也可以将其传递给其他方法和对象

答案 2 :(得分:14)

考虑像功能定义这样的方法。当两个线程运行相同的方法时,它们绝不相关。他们每个人都会创建自己的每个局部变量版本,并且无法以任何方式相互交互。

如果变量不是本地的(例如在类级别的方法之外定义的实例变量),那么它们将附加到实例(而不是单个方法的运行)。在这种情况下,运行相同方法的两个线程都看到一个变量,这不是线程安全的。

考虑以下两种情况:

public class NotThreadsafe {
    int x = 0;
    public int incrementX() {
        x++;
        return x;
    }
}

public class Threadsafe {
    public int getTwoTimesTwo() {
        int x = 1;
        x++;
        return x*x;
    }
}

在第一个中,在NotThreadsafe的同一个实例上运行的两个线程将看到相同的x。这可能很危险,因为线程正试图改变x!在第二个中,在Threadsafe的同一个实例上运行的两个线程将看到完全不同的变量,并且不会相互影响。

答案 3 :(得分:6)

除了Nambari之外的其他答案。

我想指出你可以在anoymous类型方法中使用局部变量:

这个方法可以在其他线程中调用,这可能会影响线程安全性,因此java会强制将在anoymous类型中使用的所有局部变量声明为final。

考虑这个非法代码:

public void nonCompilableMethod() {
    int i=0;
    for(int t=0; t<100; t++)
    {
      new Thread(new Runnable() {
                    public void run() {
                      i++; //compile error, i must be final:
                      //Cannot refer to a non-final variable i inside an
                      //inner class defined in a different method
                    }
       }).start();
     }
  }

如果java允许这样做(就像C#通过“闭包”那样),那么局部变量在所有情况下都不再是线程安全的。在这种情况下,所有线程末尾的i值不能保证为100

答案 4 :(得分:5)

每个方法调用都有自己的局部变量,显然,方法调用发生在一个线程中。仅由单个线程更新的变量本质上是线程安全的。

然而,请密切关注这究竟是什么意思:对变量本身的写入是线程安全的;调用它引用的对象的方法本质上不是线程安全的。同样可以直接更新对象的变量。

答案 5 :(得分:5)

线程将拥有自己的堆栈。两个线程将有两个堆栈,一个线程永远不会与其他线程共享其堆栈。局部变量存储在每个线程自己的堆栈中。这意味着线程之间永远不会共享局部变量。

答案 6 :(得分:3)

基本上四种类型的存储在java中用于存储类信息和数据:

方法区域,堆,JAVA堆栈,PC

所以方法区域和堆由所有线程共享,但每个线程都有自己的JAVA堆栈和PC,并且不会被任何其他线程共享。

java中的每个方法都是Stack框架。所以,当一个线程调用一个方法时,堆栈帧被加载到它的JAVA堆栈上。所有堆栈帧和相关操作数堆栈中的局部变量都不被其他人共享。 PC将具有下一条指令的信息,以便在方法的字节代码中执行。 所以所有局部变量都是THREAD SAFE。

@Weston也给出了很好的答案。

答案 7 :(得分:2)

我喜欢jenkov's的解释

线程堆栈包含正在执行的每个方法(调用堆栈上的所有方法)的所有局部变量。线程只能访问自己的线程堆栈。由线程创建的局部变量对创建线程之外的所有其他线程不可见。即使两个线程执行的代码完全相同,这两个线程仍将在各自的线程堆栈中创建该代码的局部变量。因此,每个线程对每个局部变量都有其自己的版本。

所有基本类型的局部变量(布尔值,字节,short,char,int,long,float,double)完全存储在线程堆栈中,因此被存储其他线程不可见。一个线程可以将一个优先变量的副本传递给另一个线程,但是它不能共享原始局部变量本身。

包含在Java应用程序中创建的所有对象,而不管创建该对象的线程如何。这包括基本类型的对象版本(例如Byte,Integer,Long等)。创建对象并将其分配给局部变量,或者将其创建为另一个对象的成员变量都没有关系,该对象仍存储在堆中。

enter image description here 局部变量可能是原始类型,在这种情况下,它会完全保留在线程堆栈中。

局部变量也可以是对对象的引用。在这种情况下,引用(本地变量)存储在线程堆栈中 ,但是对象本身(如果存储在堆中)。 >

请阅读更多-http://tutorials.jenkov.com/java-concurrency/java-memory-model.html

答案 8 :(得分:0)

本地变量存储在线程堆栈中。

局部变量({{1})(例如int,long ...)存储在primitive type上,结果-其他线程无法访问它。

局部变量thread stackreference type的后继))包含2个部分-地址(存储在Object上)和对象(其中存储在thread stack

heap

enter image description here