据我所知,final告诉CPU它可以缓存变量/对象(它无论如何都不同于CPU。我认为x86 CPU-s实际上是在做一个核心缓存L1) 我的问题是:
在下面的示例中,我将对象myObjectFinal设置为final,以便CPU可以对其进行缓存,然后更改其中的值。这是否意味着不保证myObject也会被更改?
如果我设置了final并且CPU决定对其进行缓存,那么引用是否会被破坏?
如果我更改了myObject,缓存的最终保证也会被更改?
// thread 1
volatile MyObject myObject = new MyObject();
// thread 2
final MyObject myObjectFinal = myObject;
myObjectFinal.setData(1);
// thread 1
myObject.setData(2);
也许代码并没有准确说出我的意思。因此,主要问题是:使可变对象final或volatile对CPU如何缓存对象有任何影响。如果确实如此,那么我应该在可变对象上使用final还是volatile?
CPU是否曾缓存一个可变对象,或者final / volatile对它们没有任何影响,仅用于编码一致性?
答案 0 :(得分:0)
final
表示初始化后无法更改该值。
一旦指定了最终变量,它总是包含相同的值。如果最终变量包含对对象的引用,则可以通过对对象的操作来更改对象的状态,但该变量将始终引用同一对象。 https://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.4
所以除非你重新分配myObjectFinal
,没有什么不好会发生,
但是,如果从多个线程更改同一对象,则需要同步。 (不要使用volatile
)
首先,摆脱面向对象。对象是虚拟构造。我们谈论的是低级的东西,比如寄存器和缓存 - 只有原语(每个类都可以简化为一组原语)
原因,为什么我们需要缓存很简单。 记忆力很慢。因此,我们的想法是将内存访问次数降至最低。
考虑以下代码:
1. x=0;
2. if (x == 1)
3. x++
现在正常计算机内部发生类似这样的事情(没有寄存器优化)
1. write 0 into a register
write the register into the memory
2. read x from the memory into a CPU-register
compare it with 1 and go to 3 or 4
3. read x from the memory into a CPU-register
increment register
write the register into the memory
许多冗余内存操作......但你可以消除它们
1. write 0 into a register
write the register into the memory
2. compare the register with 1 and go to 3 or 4
3. increment register
write the register into the memory
我们将x的值保留在寄存器中。那么为什么不将所有变量保存在寄存器中首先,有许多寄存器可用,所以你不能存储那里的一切。将变量保存在寄存器中更为临时。 (例如在范围内) 另一个问题是,没有其他核心和/或设备可以从寄存器中读取。那么你需要重新加载/回写变量。
如果将变量声明为final,则永远不会更改。因此,变量可以存储在寄存器中,而不会出现不一致的问题。但是这并不意味着,其他线程无法访问变量,因为它也存储在内存中。它保证了内存中的值始终有效。但是所有变量无论是否为最终变量,都可以并且将在寄存器中存储一小段时间。这就是为什么我们需要使用同步工具,如果你想保证多个线程/核心的一致性,它会强制从寄存器回写到内存。
然而,真正的CPU缓存是一个完全不同的主题。它是一个完整的透明系统(在硬件端执行!),它只受内存访问模式的影响(除非你真的在汇编中做了一些疯狂的低 - 低级别的东西)。为了简化这里:CPU缓存基本上缓存每个字节,除非你明确强制它不执行它。