100

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

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

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

有人可以告诉我。

4

8 回答 8

109

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

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

youtube 上有斯坦福教授的精彩讲座,可以帮助您理解这个概念。

于 2012-10-10T18:22:39.663 回答
19

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

public void someMethod(){

   long threadSafeInt = 0;

   threadSafeInt++;
}

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

于 2012-10-10T18:25:57.300 回答
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会看到完全不同的变量,并且不会相互影响。

于 2012-10-10T18:25:31.083 回答
6

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

但是,请密切注意这究竟是什么意思:只有对变量本身的写入是线程安全的;在它引用的对象上调用方法本质上不是线程安全的。直接更新对象的变量也是如此。

于 2012-10-10T18:33:43.350 回答
6

除了 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();
     }
  }

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

于 2012-10-13T09:38:20.580 回答
6

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

于 2013-04-17T07:08:42.700 回答
3

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

方法区、堆、JAVA栈、PC

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

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

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

于 2014-12-09T06:56:39.867 回答
2

局部变量的Java线程安全

只有 局部变量存储在线程堆栈上。

局部变量primitive type例如 int、long...)存储在thread stack并且因此 - 其他线程无法访问它。

局部变量reference type的后继Object)包含两部分 - 地址(存储在 上thread stack)和对象(存储在 上heap

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;
}

在此处输入图像描述

【JVM内存模型】

于 2019-12-29T16:38:06.963 回答