与非易失性状态的虚假共享

时间:2017-11-27 21:14:07

标签: java concurrency false-sharing

可以使用以下状态进行错误共享:

Class Foo{

   int x;
   int y;

}

两个线程同时修改x和y?或者是否无法判断编译器是否可以优化x和y到寄存器?

3 个答案:

答案 0 :(得分:1)

当然它可能会发生(可能是这里的关键字),你无法确定这两个变量是否会在同一个缓存行上最终结束。编译器不会做任何事情(至少javac)来防止将这些变量保存在不同的缓存行上的情况可能非常昂贵,并且需要大量实际需要的证据。

是的,你的评论是正确的,缓存失效会经常发生,可能成为瓶颈的原因。但是测量这个并不容易,你可以看到一个例子here

请注意,由于jdk-8有@Contended,它会填充条目以恰好适合缓存行。

答案 1 :(得分:0)

这样的测试用例:

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次)(在2核的Intel Core i5中运行):

  • volatile without padding(ms)1360 1401 1684 1323 1383
  • 带填充的挥发性物质(ms)568 612 653 638 669
  • 非挥发性无填充(ms)41 35 40 35 44
  • 带有填充的非易失性(ms)38 39 44 49 51

因此,在我看来,非易失性字段不会发生虚假共享

答案 2 :(得分:0)

我的测试证明和你的一样。

我也在使用 com.lmax.disruptor.Sequence 进行测试。

non-volatile long > volatile long use lasyset(Sequence.set) > Sequence.setVolatile == volatile long with padding > volatile long without padding