【问题标题】:Why are local variables thread safe in Java为什么局部变量在 Java 中是线程安全的
【发布时间】:2012-10-01 07:23:48
【问题描述】:

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

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

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

谁能告诉我。

【问题讨论】:

  • 因为它们是在堆栈中分配的。并且线程不共享堆栈..每个堆栈都是独一无二的..

标签: java thread-safety


【解决方案1】:

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

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

Stanford professor in youtube 的精彩讲座可以帮助您理解这个概念。

【讨论】:

  • 对不起,你错了,只有原始局部变量存储在堆栈上。其余所有变量都存储在堆上。 Java 7 引入了转义分析,对于某些变量可能会在堆栈中分配它
  • 堆栈仅保存对堆上对象的引用。因为堆栈被清除,所以引用也被清除。因此它可用于垃圾收集
  • @Jatin:你是对的。当我指的是内存时,我指的是对象的引用值和原语的值(我认为新手开发人员也知道对象在堆上)。
  • @Nambari 但如果引用值指向共享变量。那怎么能说它是线程安全的呢?
  • @hajder:是什么让变量共享?从那里开始。实例变量或类变量对吗?不是局部变量并阅读此线程中的 Marko Toplink 答案,我认为这是您感到困惑的地方。
【解决方案2】:

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

public void someMethod(){

   long threadSafeInt = 0;

   threadSafeInt++;
}

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

【讨论】:

  • agrument有错误,请看@Nambari响应的cmets
  • 如果您指出 localSafeInt 总是为 0,然后为 1,然后无论如何都被删除,那很好。所以它表明这个变量不在线程之间共享,因此不受多线程的影响。我想你可以多指出一点,线程安全总是只是 0 或 1
【解决方案3】:

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

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

考虑这两种情况:

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

【讨论】:

    【解决方案4】:

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

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

    【讨论】:

    • 您说“在它引用的对象上调用方法本质上不是线程安全的”。但是,方法本地引用所引用的对象(在此方法范围内实例化)如何由两个线程共享?你能举个例子吗?
    • 局部变量可能包含也可能不包含在方法范围内实例化的对象,这不是问题的一部分。即使是,该方法也可以访问共享状态。
    【解决方案5】:

    除了 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

    【讨论】:

    • 嗨 Weston,从上面的讨论和下面的答案中,我了解到 java 确保所有局部变量的线程安全。我可以知道 synchronized 关键字的实际用途是什么吗?你能用一个像这样的例子来解释一下吗?
    【解决方案6】:

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

    【讨论】:

      【解决方案7】:

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

      方法区、堆、JAVA栈、PC

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

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

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

      【讨论】:

        【解决方案8】:

        局部变量的Java线程安全

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

        局部变量,即 primitive type(例如 int、long...)存储在 thread stack 上,因此 - 其他线程无法访问它。 p>

        局部变量,即reference typeObject 的继任者)包含两部分 - 地址(存储在 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 Memory model]

        【讨论】:

          猜你喜欢
          • 2017-03-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-07-07
          • 1970-01-01
          相关资源
          最近更新 更多