我正在阅读Java中的多线程,我遇到了这个
本地变量在Java中是线程安全的。
从那时起,我一直在思考如何/为什么局部变量是线程安全的。
请有人告诉我。
答案 0 :(得分:93)
创建线程时,它将创建自己的堆栈。两个线程将有两个堆栈,一个线程永远不会与其他线程共享其堆栈。
程序中定义的所有局部变量都将在堆栈中分配内存(如Jatin所述,这里的内存意味着对象的引用值和基本类型的值)(线程的每个方法调用都会自己创建一个堆栈帧堆)。一旦该线程完成方法执行,就会删除堆栈帧。
Stanford professor in youtube有很棒的讲座可以帮助你理解这个概念。
答案 1 :(得分:17)
局部变量存储在每个线程自己的堆栈中。这意味着线程之间永远不会共享局部变量。这也意味着所有本地原始变量都是线程安全的。
public void someMethod(){
long threadSafeInt = 0;
threadSafeInt++;
}
对象的本地引用略有不同。引用本身不共享。但是,引用的对象不存储在每个线程的本地堆栈中。所有对象都存储在共享堆中。如果本地创建的对象永远不会转义它创建的方法,那么它是线程安全的。实际上,只要这些方法或对象都不会使传递的对象可用于其他线程,您也可以将其传递给其他方法和对象
答案 2 :(得分: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
的同一个实例上运行的两个线程将看到完全不同的变量,并且不会相互影响。
答案 3 :(得分: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();
}
}
如果java允许这样做(就像C#通过“闭包”那样),那么局部变量在所有情况下都不再是线程安全的。在这种情况下,所有线程末尾的i
值不能保证为100
。
答案 4 :(得分:5)
每个方法调用都有自己的局部变量,显然,方法调用发生在一个线程中。仅由单个线程更新的变量本质上是线程安全的。
然而,请密切关注这究竟是什么意思:仅对变量本身的写入是线程安全的;调用它引用的对象的方法本质上不是线程安全的。同样可以直接更新对象的变量。
答案 5 :(得分:5)
线程将拥有自己的堆栈。两个线程将有两个堆栈,一个线程永远不会与其他线程共享其堆栈。局部变量存储在每个线程自己的堆栈中。这意味着线程之间永远不会共享局部变量。
答案 6 :(得分:3)
基本上四种类型的存储在java中用于存储类信息和数据:
方法区域,堆,JAVA堆栈,PC
所以方法区域和堆由所有线程共享,但每个线程都有自己的JAVA堆栈和PC,并且不会被任何其他线程共享。
java中的每个方法都是Stack框架。所以,当一个线程调用一个方法时,堆栈帧被加载到它的JAVA堆栈上。所有堆栈帧和相关操作数堆栈中的局部变量都不被其他人共享。 PC将具有下一条指令的信息,以便在方法的字节代码中执行。 所以所有局部变量都是THREAD SAFE。
@Weston也给出了很好的答案。答案 7 :(得分:2)
我喜欢jenkov's的解释
线程堆栈包含正在执行的每个方法(调用堆栈上的所有方法)的所有局部变量。线程只能访问自己的线程堆栈。由线程创建的局部变量对创建线程之外的所有其他线程不可见。即使两个线程执行的代码完全相同,这两个线程仍将在各自的线程堆栈中创建该代码的局部变量。因此,每个线程对每个局部变量都有其自己的版本。
所有基本类型的局部变量(布尔值,字节,short,char,int,long,float,double)完全存储在线程堆栈中,因此被存储其他线程不可见。一个线程可以将一个优先变量的副本传递给另一个线程,但是它不能共享原始局部变量本身。
堆包含在Java应用程序中创建的所有对象,而不管创建该对象的线程如何。这包括基本类型的对象版本(例如Byte,Integer,Long等)。创建对象并将其分配给局部变量,或者将其创建为另一个对象的成员变量都没有关系,该对象仍存储在堆中。
局部变量可能是原始类型,在这种情况下,它会完全保留在线程堆栈中。
局部变量也可以是对对象的引用。在这种情况下,引用(本地变量)存储在线程堆栈中 ,但是对象本身(如果存储在堆中)。 >
请阅读更多-http://tutorials.jenkov.com/java-concurrency/java-memory-model.html
答案 8 :(得分:0)