在以下状态下是否会发生错误共享:
Class Foo{
int x;
int y;
}
两个线程同时修改 x 和 y?还是无法判断编译器可能会将 x 和 y 优化为寄存器?
在以下状态下是否会发生错误共享:
Class Foo{
int x;
int y;
}
两个线程同时修改 x 和 y?还是无法判断编译器可能会将 x 和 y 优化为寄存器?
当然它可能会发生(可能是这里的关键字),你不能确定这两个变量是否会明显地出现在同一个缓存行上。编译器不会做任何事情(至少javac
)来防止这样的情况,例如让这些变量保存在不同的缓存行上可能会非常昂贵,并且需要大量实际需要的证据。
是的,您的评论是正确的,缓存失效会经常发生,并且可能是造成瓶颈的原因。但是测量这并不容易,你可以在这里看到一个例子。
请注意,由于 jdk-8 存在@Contended
将填充条目以恰好适合缓存行。
像这样的测试用例:
public class FalseSharing implements Runnable {
public final static int NUM_THREADS = 2; // change
public final static long ITERATIONS = 50L * 1000L * 1000L;
private static VolatileLong[] longs = new VolatileLong[NUM_THREADS];
static {
for (int i = 0; i < longs.length; i++) {
longs[i] = new VolatileLong();
}
}
private final int arrayIndex;
public FalseSharing(final int arrayIndex) {
this.arrayIndex = arrayIndex;
}
public static void main(final String[] args) throws Exception {
final long start = System.currentTimeMillis();
runTest();
System.out.println("duration = " + (System.currentTimeMillis() - start));
}
private static void runTest() throws InterruptedException {
Thread[] threads = new Thread[NUM_THREADS];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(new FalseSharing(i));
}
for (Thread t : threads) {
t.start();
}
for (Thread t : threads) {
t.join();
}
}
public void run() {
long i = ITERATIONS + 1;
while (0 != --i) {
longs[arrayIndex].value = i;
}
}
public final static class VolatileLong {
public long p1, p2, p3, p4, p5, p6, p7; // padding
public long value = 0L; // change to volatile when test
public long p1_1, p2_1, p3_1, p4_1, p5_1, p6_1, p7_1; // padding
}}
测试结果(5 次)(在 Intel Core i5 2 核中运行):
所以在我看来,非易失性字段不会发生虚假共享
我的测试证明和你的一样。
我也在使用 com.lmax.disruptor.Sequence 进行测试。
non-volatile long > volatile long 使用 lasset(Sequence.set) > Sequence.setVolatile == volatile long with padding > volatile long without padding