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

Ana*_*and 86 java thread-safety

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

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

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

有人可以让我知道.

kos*_*osa 97

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

在你的程序中定义的所有局部变量将被分配在堆栈存储器(如Jatin评论,这里存储装置,对象和基本类型值的参考值)(由主题中的每个方法调用创建其自己的堆栈一个堆栈帧).一旦该线程完成方法执行,就会删除堆栈帧.

斯坦福教授在youtube上有很棒的演讲,可以帮助你理解这个概念.

  • 对不起,你错了,只有原始的局部变量存储在堆栈上.休息所有变量都存储在Heap上.Java 7引入了转义分析,对于某些变量可能会在堆栈中分配它 (13认同)
  • 堆栈仅保存对堆上对象的引用.因为堆栈被清除,所以引用也被清除.因此它可用于垃圾收集 (6认同)
  • @Jatin:你是对的.当我的意思是内存时,我指的是对象的引用值和基元的值(我认为新手开发人员也知道对象在堆上). (6认同)
  • @hajder:什么使变量共享?从那里开始.实例或类变量对不对?不是本地变量并且在这个帖子中读过Marko Toplink答案,我认为这是你感到困惑的一点. (3认同)
  • @Nambari,但如果参考值指向共享变量。那我们怎么可以说它是线程安全的呢? (2认同)

Ren*_*ith 17

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

public void someMethod(){

   long threadSafeInt = 0;

   threadSafeInt++;
}
Run Code Online (Sandbox Code Playgroud)

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


Cor*_*all 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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


wes*_*ton 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();
     }
  }
Run Code Online (Sandbox Code Playgroud)

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


Mar*_*nik 5

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

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


Sud*_*ute 5

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