程序问答   发布时间:2022-06-02  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了Java 多线程值损坏大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

如何解决Java 多线程值损坏?

开发过程中遇到Java 多线程值损坏的问题如何解决?下面主要结合日常开发的经验,给出你关于Java 多线程值损坏的解决方法建议,希望对你解决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是如何工作的:

  • 方法是“可重入的”,每次方法运行时,每个局部变量和参数都是唯一的副本。那是因为这些是由堆栈上的东西表示的,您当然可以继续向其中添加东西。从这个意义上说,堆栈是一个相对的东西:“System.out.println(b)”,如果 b 是一个局部变量或参数,就像一条指令读取一本书中的一行'两行以上您当前正在阅读”(只要您继续阅读,这将是一个新行),而不是阅读“本书第 8 行”的说明,每次都是同一行。
  • Java 是按值传递的,这意味着你得到的一切都是一个副本:
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,请注明来意。