我正在阅读Java中的多线程,并发现了这个说法:
在Java中,局部变量是线程安全的。
从那以后,我一直在思考局部变量为什么是线程安全的,以及如何实现线程安全。
请问有人能告诉我吗?
我正在阅读Java中的多线程,并发现了这个说法:
在Java中,局部变量是线程安全的。
从那以后,我一直在思考局部变量为什么是线程安全的,以及如何实现线程安全。
请问有人能告诉我吗?
本地变量存储在每个线程自己的堆栈中,这意味着本地变量永远不会在线程之间共享。这也意味着所有本地原始变量都是线程安全的。
public void someMethod(){
long threadSafeInt = 0;
threadSafeInt++;
}
本地对象的引用有所不同。 引用本身不是共享的。 然而,被引用的对象并没有存储在每个线程的本地堆栈中,而是存储在共享堆中。 如果在创建本地对象后,它从未逃逸到创建它的方法之外,则它是线程安全的。 实际上,只要这些方法或对象中没有一个使传递的对象可供其他线程使用,您也可以将其传递给其他方法和对象
把方法看做功能的定义。当两个线程运行同一个方法时,它们并没有关联。它们将分别创建自己版本的每个局部变量,并且无法以任何方式相互交互。
如果变量不是局部变量(例如在类级别方法外定义的实例变量),那么它们将附加到该实例上(而不是单独运行方法)。在这种情况下,运行相同方法的两个线程都会看到同一个变量,这就不是线程安全的。
考虑以下两种情况:
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
实例上运行的两个线程将看到完全不同的变量,并且彼此不能影响。
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
。
线程将拥有自己的栈。两个线程将拥有两个栈,一个线程永远不会与其他线程共享其栈。局部变量存储在每个线程自己的栈中。这意味着局部变量永远不会在线程之间共享。
每个方法调用都有自己的本地变量,显然,方法调用发生在单个线程中。只由单个线程更新的变量天生就是线程安全的。
然而,要密切关注什么确切地被认为是“只有”:仅对变量本身的写操作是线程安全的;调用其引用对象的方法本质上不是线程安全的。直接更新对象变量也是如此。
Java中有四种类型的存储方式来存储类信息和数据:
方法区,堆,JAVA栈,程序计数器
因此,方法区和堆是所有线程共享的,但每个线程都有自己的JAVA栈和程序计数器,并且不与其他线程共享。
Java中的每个方法都是一个栈帧。当一个线程调用一个方法时,该栈帧会加载到它的JAVA栈上。该栈帧中的所有本地变量和相关的操作数栈不会被其他线程共享。程序计数器将包含在方法字节码中执行下一条指令的信息。因此,所有的局部变量都是线程安全的。
@Weston 也给出了很好的答案。
Java本地变量的线程安全
只有本地变量存储在线程栈上。
基本类型的本地变量(例如int、long等)存储在线程栈中,因此其他线程无法访问它。
引用类型(继承自Object
)的本地变量包含两部分-地址(存储在线程栈中)和对象(存储在堆中)。
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;
}