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

为什么Java中的局部变量是线程安全的,java,thread-safety,Java,Thread Safety,我在阅读Java中的多线程技术时遇到了这个问题 局部变量在Java中是线程安全的 从那以后,我一直在思考局部变量是如何/为什么是线程安全的 有人能告诉我吗。当你创建一个线程时,它会创建自己的堆栈。两个线程将有两个堆栈,一个线程从不与另一个线程共享其堆栈 程序中定义的所有局部变量都将在堆栈中分配内存(正如Jatin所评论的,这里的内存意味着对象的参考值和基元类型的值)(线程的每个方法调用都会在自己的堆栈上创建一个堆栈帧)。此线程完成方法执行后,堆栈帧将被删除 有一个很好的讲座可以帮助你理解这个概念

我在阅读Java中的多线程技术时遇到了这个问题

局部变量在Java中是线程安全的

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


有人能告诉我吗。

当你创建一个线程时,它会创建自己的堆栈。两个线程将有两个堆栈,一个线程从不与另一个线程共享其堆栈

程序中定义的所有局部变量都将在堆栈中分配内存(正如Jatin所评论的,这里的内存意味着对象的参考值和基元类型的值)(线程的每个方法调用都会在自己的堆栈上创建一个堆栈帧)。此线程完成方法执行后,堆栈帧将被删除


有一个很好的讲座可以帮助你理解这个概念。

想想功能定义之类的方法。当两个线程运行相同的方法时,它们之间没有任何关联。它们将各自创建各自的局部变量版本,并且将无法以任何方式相互交互

如果变量不是局部变量(如在类级别的方法外部定义的实例变量),则它们将附加到实例(而不是方法的单个运行)。在本例中,运行同一方法的两个线程都看到一个变量,这不是线程安全的

考虑以下两种情况:

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
实例上运行的两个线程将看到完全不同的变量,并且不会相互影响。

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

public void someMethod(){

   long threadSafeInt = 0;

   threadSafeInt++;
}

对对象的本地引用有点不同。引用本身不共享。但是,引用的对象并不存储在每个线程的本地堆栈中。所有对象都存储在共享堆中。如果在本地创建的对象从未逃逸创建它的方法,那么它是线程安全的。事实上,您也可以将其传递给其他方法和对象,只要这些方法或对象中没有一个使传递的对象可供其他线程使用

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


但是,请密切注意这句话的确切含义:只有对变量本身的写入是线程安全的;对它引用的对象调用方法本身不是线程安全的。直接更新对象变量也是如此。

除了其他答案,如Nambari的答案

我想指出,您可以在异常类型方法中使用局部变量:

此方法可以在其他线程中调用,这可能会危及线程安全,因此java强制将异常类型中使用的所有局部变量声明为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();
     }
  }
public void非编译方法(){
int i=0;

对于(int t=0;t线程将有自己的堆栈。两个线程将有两个堆栈,一个线程从不与其他线程共享其堆栈。局部变量存储在每个线程自己的堆栈中。这意味着局部变量永远不会在线程之间共享。

java中基本上有四种存储类型来存储类信息和数据:

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

因此,方法区域和堆由所有线程共享,但每个线程都有自己的JAVA堆栈和PC,其他线程不共享

java中的每个方法都作为堆栈帧。因此,当一个线程调用一个方法时,该堆栈帧将加载到其java堆栈上。该堆栈帧中的所有局部变量和相关操作数堆栈不会被其他线程共享。 PC将在方法的字节码中包含要执行的下一条指令的信息。 所以所有的局部变量都是线程安全的


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

线程堆栈上只存储了局部变量

局部变量
基元类型
(例如int、long…)存储在
线程堆栈
上,因此其他线程无法访问它

局部变量
引用类型
对象
)包含两部分-地址(存储在
线程堆栈
)和对象(存储在


因为它们是在堆栈中分配的。线程不共享堆栈。每个线程都是唯一的。很抱歉,您错了,只有原始的局部变量存储在堆栈上。其余所有变量都存储在堆栈上。Java 7引入了转义分析,对于某些变量,它可能在堆栈中分配。堆栈仅保留对堆栈上对象的引用由于堆栈被清除,引用也被清除。因此它可用于垃圾collection@Jatin:你说得对。当我指的是内存时,我指的是对象的参考值和原语的值(我认为新手开发人员也知道对象在堆上)@Nambari但如果引用值指向共享变量,那么我们怎么能说它是线程安全的呢?@hajder:什么使变量成为共享的?从那里开始。实例或类变量对吗?不是局部变量,请阅读此线程中的Marko Toplink答案,我想这是您感到困惑的一点。在grument,pl看看@Nambari responsei的评论,如果你指的是localSafeInt也会是0,然后是1,然后被删除,这很好

class MyRunnable implements Runnable() {
    public void run() {
        method1();
    }

    void method1() {
        int intPrimitive = 1;

        method2();
    }

    void method2() {
        MyObject1 myObject1 = new MyObject1();
    }
}

class MyObject1 {
    MyObject2 myObject2 = new MyObject2();
}

class MyObject2 {
    MyObject3 myObject3 = MyObject3.shared;
}

class MyObject3 {
    static MyObject3 shared = new MyObject3();

    boolean b = false;
}