首页 文章

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

提问于
浏览
78

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

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

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

有人可以告诉我 .

8 回答

  • 5

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

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

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

  • 86

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

    public void someMethod(){
    
       long threadSafeInt = 0;
    
       threadSafeInt++;
    }
    

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

  • 5

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

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

    考虑这两种情况:

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

  • 14

    除了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#通过"closures"),那么局部变量在所有情况下都不再是线程安全的 . 在这种情况下,所有线程末尾的 i 的值不保证为 100 .

  • 2

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

    However ,密切关注这究竟是什么意思: only 对变量本身的写入是线程安全的;调用它引用的对象的方法 is not inherently thread-safe . 直接更新对象的变量也是如此 .

  • 6

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

  • 2

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

    Method Area,Heap,JAVA Stack,PC

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

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

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

  • 18

    我喜欢jenkov's的解释

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

    primitive types 的所有局部变量(boolean,byte,short,char,int,long,float,double)都完全存储 on the thread stack ,因此对其他人不可见线程 . 一个线程可以将一个pritimive变量的副本传递给另一个线程,但它不能共享原始局部变量本身 .

    The heap 包含在Java应用程序中创建的 all objects ,无论创建该对象的线程是什么 . 这包括基元类型的对象版本(例如Byte,Integer,Long等) . 无论是创建对象并将其分配给局部变量,还是创建为另一个对象的成员变量,该对象仍然存储在堆上 .

    enter image description here
    局部变量可能是 primitive type ,在这种情况下,它完全保持 on the thread stack .

    局部变量也可以是对象的 reference . 在这种情况下,引用(局部变量)存储 on the thread stack ,但 object 本身存储 on the heap .

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

相关问题