我想了解Java如何深入工作。所有教程都处于过高的水平,所以我有几个问题:
鉴于此代码:
class Example {
public void foo() {
int number = getRandomNumber();
System.out.println(number);
}
}
假设在上面的示例中,多个线程可以访问foo()
方法。每个线程都有自己正确的number
变量值,或者第二个线程是否可以修改第一个线程的值(因此它们都打印相同的值)?
如果int number
是最终的,会发生什么?
我想知道的主要事情是,当我有一些初始化(连接,......)时,我想确保线程不会相互干扰。
答案 0 :(得分:6)
每个线程都有自己的正确的数字变量值,或者第二个线程是否可以修改第一个线程的值(所以它们都打印相同的值)?
将 没有其他线程可以更改局部变量的值,也不能在任何其他代码中更改一次调用的值。它是该调用状态的一部分。 如果int号是最终的,会发生什么? 这会阻止你在初始化后改变变量的值,基本上。foo
的每个方法调用都有自己的number
变量更清楚地认为它是方法中的局部变量。因此,如果这是一种递归方法,那么即使在单个线程中,你最终也可能同时拥有多个number
变量,因为它们是方法的不同调用。 / p>
答案 1 :(得分:2)
方法中的所有本地成员都将处于堆叠状态,因此每个方法都有自己的数字变量副本。
如果number是最终的,那么每个线程仍然会有自己的number副本.Declaring final只会告诉编译器这个变量不会改变,所以编译器可以对他进行优化。
答案 2 :(得分:1)
在 方法中声明 的所有变量都是堆栈本地。进入该方法的每个线程将为那些对其他线程完全不可见的变量获取自己的值。声明变量final并不会改变...它只影响你可以访问这些变量,例如,在匿名内部类中。
现在,你可能可能与从getRandomNumber
方法检索的值有一些并发问题 - 取决于它的实现,并且它访问共享(类)状态。但那是理论,因为你没有展示它的实现。
对于grins和giggles,这是显示本地方法堆栈的字节代码:
public class MyClass {
public void myMethod() {
int tField = 10;
System.out.println(tField);
}
}
字节码:
public void myMethod();
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=1 // <-- Each thread gets its own
0: bipush 10
2: istore_1
3: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
6: iload_1
7: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
10: return
答案 3 :(得分:1)
答案 4 :(得分:1)
number
是一个局部变量,而不是一个字段,因此它存在于堆栈中,每个线程都有自己的版本。
您只需要考虑共享状态的线程安全性,即在堆上分配的数据,例如对象的字段。
答案 5 :(得分:1)
回答关于number
的两个问题 - 每个线程都有自己的变量 - 没有线程可以改变任何其他线程局部变量,无论它是否为final
。
所有局部变量都是唯一的 - 对于调用该方法的每个线程,堆栈上都有一个副本。
答案 6 :(得分:1)
每个线程都有自己的调用堆栈。
在该调用堆栈上有堆栈帧。每个方法调用一个。
在堆栈框架内部是局部变量(除其他外)。
所以,是的,foo
方法的每次调用都有自己的number
变量。
拥有final
局部变量并不会从根本上改变它。
答案 7 :(得分:1)
根据示例代码
在方法上下文中,并发访问foo()
的线程将拥有自己的堆栈来执行foo()
方法。不共享局部变量。
对于并发访问而言,没有任何问题。最后一次访问将确保内部foo()
方法编号值不会更新。
如果跨多个线程共享对象或静态变量,则会出现并发场景,线程可能会干扰。
下面,如果两个线程共享类XYZ的对象,则一个线程更新的编号可能会被第二个线程打印。
class XYZ {
static int number = 0;
public void foo() {
number = getRandomNumber();
System.out.println(number);
}
}