线程缓存和Java内存模型

我正在尝试理解Java内存模型和线程。 据我所知,每个线程都有一个“主”内存的本地副本。 因此,如果一个线程试图更改一个int变量(例如某个对象),它会缓存int变量,如果它更改了它,则其他线程可能看不到更改。 但是如果线程缓存某些对象而不是int呢? 在这种情况下,哪些线程会缓存它? 如果一个线程缓存对象的引用,对对象状态的任何更改都不会被其他线程看到? 为什么?

先谢谢你

CPU具有不同级别的高速缓存L1,L2,L3。 每个CPU(以及/或CPU核心)都有自己的缓存。 此高速缓存存储最小的主存储器(RAM)以提高性能。

  _______________ ______________ | CPU 1 | | CPU 2 | | _________ | | _________ | | | Level 1 | | | | Level 1 | | | | Cache | | | | Cache | | | | | | | | | | | |_________| | | |_________| | |_______________| |______________| | | | | | | | | _|_|______________|_|__ | | | MAIN MEMORY | |_______________________| Time Command CPU 1 (Cache) CPU 2 (Cache) Main Memory ------- ---------- ---------------- -------------- ------------- 1 --- --- --- x = 10 2 Read x (on cpu1) x = 10 --- x = 10 3 Write x <--20 (on cpu1) x = 20 --- x = 10 4 Read x (on cpu2) x = 20 x = 10 x = 10 5 put cache to Main mem x = 20 x = 10 x = 20 

例如,在执行顺序上,CPU2上的x值是错误的。 x值已由CPU1更改。 如果x变量定义为volatile,则所有写操作立即反映到主存。

线程没有本地内存副本。 线程读/写的部分内存可以来自缓存,而不是主内存。 高速缓存不需要彼此同步,也不需要与主存储器同步。 所以这是你可以观察到不一致的地方。

因此,如果一个线程试图更改一个int变量,例如某个对象,它会缓存int变量,如果它更改它,其他线程可能看不到更改。

那是对的。 Java存储器模型在规则之前定义,例如,在字段x的易失性写入和字段x的易失性读取之间的规则之前发生。 因此,当写入完成后,后续读取将看到写入的值。

如果在关系之前没有这样的事情发生,所有的赌注都会被取消(当规则之前没有发生时,指令重新排序也会使生活变得复杂)。

如果线程缓存对对象的引用,对对象状态的任何更改对其他线程也不可见? 为什么?

它可能是可见的……它也可能不可见。 所有投注都没有在规则之前发生。 之所以如此,那么很多优化就像硬件技巧一样加快速度,或者不允许使用编译器技巧。 当然,始终保持内存与缓存同步会降低性能。

CPU有多个缓存。 正是这些硬件缓存可能具有不一致的数据副本。 它们可能不一致的原因是保持一致性会使代码速度降低10倍,并破坏从multithreading中获得的任何好处。 要获得不错的性能,您需要有选择性地保持一致。 Java内存模型描述了它何时确保数据是一致的,但在最简单的情况下却没有。

注意:这不仅仅是CPU问题。 可以在代码中内联不必在线程之间保持一致的字段。 这可能意味着如果一个线程更改了该值,则另一个线程可能永远不会看到此更改,因为它已被烧入代码中。

“然而,在编写合适的multithreading代码之前,您确实需要更多地研究multithreading代码的复杂性和细微之处。

说到线程,保证很少。

你能想象当两个不同的线程可以访问一个类的单个实例时可能发生的破坏,两个线程都调用该对象上的方法……这些方法是否会修改对象的状态? ……甚至可视化也太可怕了。“, 来自Sun认证程序员Java 6,第9章:线程。

我的朋友,

在Java中,线程不会缓存任何对象或变量,它们只是 对象 实例引用 。 谈论线程缓存内存更像是在讨论操作系统线程 ……无论内部如何管理线程 ,Java在所有操作系统中都以相同的方式工作,这在很大程度上取决于不同的操作系统。

看一下这段代码:

 AccountDanger r = new AccountDanger(); Thread one = new Thread(r): Thread two = new Thread(r); 

如您所见,在这种情况下,线程可以访问同一个实例: r 。 然后, 你会遇到同步问题 ,肯定……如果我们谈论本机或对象成员并不重要,第一和第二个线程可以访问r的所有成员(如果它们可以通过作用域或setter / getters访问) )他们将直接读取r实例中的值。 即使你没有注意到它,这肯定是肯定的,有时候真的很难。

如果您想编写multithreading应用程序,我建议您阅读有关java作用域Java同步的内容

问候,

每个线程都没有本地内存副本。 如果变量对多个线程可见(因为其范围),则每个线程都会看到相同的值。

但是,multithreading程序需要非常小心共享变量(内存),因为如果你不小心,很容易引入竞争条件。