大佬教程收集整理的这篇文章主要介绍了Java 多线程值损坏,大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
向Far提问。
给出的代码是
public class MathUtils {
public static BigInteger factorial(int number) {
BigInteger f = new BigInteger("1");
for (int i = 2; i <= number; i++) {
f = f.multiply(BigInteger.valueOf(i));
}
return f;
}
}
上面链接的网站说它是无状态的,多个线程可以同时运行此方法并获得正确的结果。我的问题是:当许多线程调用它时,number
变量的值不会被破坏吗?
您需要了解的具体机制是堆栈。
每个线程都有自己的堆栈。堆栈是传统的后进先出设置:您可以在其上“推送”内容,也可以从其“弹出”内容,这将检索并删除最近推送的内容。
每个线程都有自己独特的堆栈。堆栈用于局部变量和执行指针。想象一下这段代码:
public static void main(String[] args) {
a(5);
System.out.println("Done");
}
public static void a(int X) {
b();
System.out.println("In a: " + a);
}
public static void b() {}
现在假设你是一个 CPU。你只是指向一条指令,应该运行它。你不知道java,也不知道什么是循环。您只知道基本说明,包括“转到”。但这就是你所拥有的。
一旦 b() 运行完毕,你怎么知道返回到哪里?您怎么知道必须跳回 a 方法的中点并在 System.out.println("In a")
处继续?
堆栈就是答案。执行 b()
时,幕后发生的事情是:
PUSH [position in this method we're at right now]
GOTO [position of the start of the b method]
并且 b() 方法以一条指令结束,该指令表示:从堆栈中弹出一个数字,然后转到该数字。
局部变量也存储在堆栈中。所以,这一切的指令集本质上是:
1 PUSH 4 // position to return BACk to once a is done
2 PUSH 5 // from a(5)
3 GOTO 8 // a method
4 PUSH 7
5 CREATE_OBjeCT "Done" // pushes pos of new object on stack
6 GOTO [position of System.out.println]
7 EXIT_APPLICATION
8 PUSH 10 // position to return BACk to
9 GOTO 16 // b() method
10 CREATE_OBjeCT "In a: " // pushes pos of new object on stack
11 FLIP // flip the top two stack entries
12 CONCAT_StriNGS
13 PUSH 15 // position to return BACk to
14 GOTO [position of System.out.println]
15 RET // pop number and go to it
16 RET
(这过于简单化了;CPU 和字节码比这复杂得多,但希望能说明问题!)
栈作为一个概念解释了java是如何工作的:
b
是一个局部变量或参数,就像一条指令读取一本书中的一行'两行以上您当前正在阅读”(只要您继续阅读,这将是一个新行),而不是阅读“本书第 8 行”的说明,每次都是同一行。int x = 5;
add1(X);
System.out.println(X);
public void add1(int X) {
x = x + 1;
}
上面打印的是 5 而不是 6,因为 add1(X)
是:
PUSH current_value_of_whatever_the_x_variable_holds
CALL add1
并且 add1 将对该推入的值进行操作,而不是对您的 x 变量进行操作。当我们涉及到对象时,它会变得有点复杂(因为 Java 中的对象是通过它们的引用来表示的:一个指针。想象一个对象是一所房子,而一个引用更像是一个地址。我可以有一个到我家的地址,然后给你一张纸上那个地址的副本。你可以拿笔随意更换那张纸,这不会影响我的地址簿或我的房子。但如果你开车过去到房子里,从窗户扔一块砖,即使我递给你我地址簿的一页副本,那仍然是我的窗户。所以:
List<String> list = new ArrayList<String>();
list.add("Hello");
add1(list);
System.out.println(list);
public void add1(List<String> list) {
list.add("World!");
}
这会打印Hello,World!
。因为 .
在 Java 中相当于“开车到地址 list
指向的房子”。如果我写了 list = List.of("Hello","World!")
,似乎什么都不会发生,因为 =
相当于 java:擦除地址卡并在上面写一个新地址。这不会影响我的房子和我的地址簿。
多线程确实可以安全地调用@H_540_7@mathUtils.factorial()。
每次激活 factorial
都会有自己的 f
副本,可以单独访问。这就是“局部变量”的含义。
参数 number
没有被修改,在任何情况下都像一个局部变量“factorial”。
关于您在评论中的问题。不,代码只有一份副本——不需要多一份。但是每个线程都有自己的代码执行方式,因此如果将其视为“单独的副本”会有所帮助,那么概念上的危害就不会太大。
,每当任何线程(除了主线程)开始执行“factorial(int number)”方法时,该线程都会将“number”的副本作为局部变量保存到它自己的堆栈中,因此没有机会更改该值任何其他线程的“数字”。
但是,如果“number”值来自任何共享对象(由多个线程共享),并且如果它被多个线程复制到堆栈中,并且在该值被某些线程更改之后,则在这种情况下可能存在数据不一致的可能性(选中“易失性”)。
以上是大佬教程为你收集整理的Java 多线程值损坏全部内容,希望文章能够帮你解决Java 多线程值损坏所遇到的程序开发问题。
如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。