在作为JSR166的一部分引入的类中,作者使用所谓的填充来填充Striped64.Cell类的单个值字段。
以下是这门课的节选:
/**
* Padded variant of AtomicLong supporting only raw accesses plus CAS. The value field is placed
* between pads, hoping that the JVM doesn't reorder them.
* <p/>
* JVM intrinsics note: It would be possible to use a release-only form of CAS here, if it were
* provided.
*/
static final class Cell {
volatile long p0, p1, p2, p3, p4, p5, p6;
volatile long value;
volatile long q0, q1, q2, q3, q4, q5, q6;
...然后,作者使用CAS原子化地修改该值。
在Striped64类中,作者还使用不安全来访问其他两个字段,但没有应用任何这样的填充。
我的问题是:为什么要做这样的事情,引入14个冗余字段来填充单个值字段?
发布于 2014-02-27 13:15:56
填充是为了防止共享value字段所在的高速缓存行,否则该值可能必须从内存中重新获取,因为缓存行上的其他内容要求整个行无效。因此,我们的目标是提高性能。
为了让事情变得更简单,Java8引入了 annotation,它在幕后做同样的事情,只是它是由JVM自己处理的。
发布于 2014-02-28 11:55:58
虽然我同意亚述人的回答,但我认为这需要一些解释。
为什么缓存丢失很重要?
因为从主内存读取比从缓存读取要慢得多。如果您有一个需要经常使用的变量,那么让它缓存是很重要的。另外,如果该变量与其他变量共享相同的缓存,则可能会出现整个缓存行可能失效的情况。
考虑一下当variable1与variable2驻留在同一个缓存中时的例子。variable1由thread1使用,variable2由thread2使用。因为它们位于相同的缓存行上,所以如果有variable2的更新,而thread1需要使用variable1,则需要删除缓存行(即使它不使用这个变量!)并从主内存中读取。这被称为false共享。
为什么实际上还有7个长?
如果只有JVM没有决定重新排序内存,那么从哪里开始读取这个变量并不重要(您可以从第三行读取它,从8个“缓存行”中读取它)--在缓存行中仍然有一个值。所以,不管你是从哪里开始阅读的,只有一个对你很重要的值会出现在缓存线上,因此不可能出现“错误共享”的“缓存丢失”。
这就是为什么Java对象的大小可以除以8的原因。
https://stackoverflow.com/questions/22069423
复制相似问题