發生場景
多核 CPU 下多個線程之間改寫連續不同資源。
發生機制
因為 CPU 往往使用高速緩衝來加快對變量的讀取,而 MESI 協議會導致不同 CPU 上的線程修改對應的高速緩存時,會使其它 CPU 上的相應的高速緩存行失效(如果存在),那麼另一個 CPU 修改該高速緩衝行中的變量時(即使不是同一個變量)就會從內存中進行讀取,這樣又會導致原來 CPU 的對應緩衝行失效,如此反復,導致 CPU 對於變量的修改讀取都需要經過內存而無法有效利用高速緩衝,從而減慢速度。
代碼實現
- 發生偽填充
public final class FalseSharing
implements Runnable
{
public final static int NUM_THREADS = 2; // change
public final static long ITERATIONS = 500 * 1000 * 1000;
private final int arrayIndex;
private static VolatileLong[] longs = new VolatileLong[NUM_THREADS];
static
{
for (int i = 0; i < longs.length; i++)
{
longs[i] = new VolatileLong();
}
}
public FalseSharing(final int arrayIndex)
{
this.arrayIndex = arrayIndex;
}
public static void main(final String[] args) throws Exception
{
final long start = System.nanoTime();
runTest();
System.out.println("duration = " + (System.nanoTime() - 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 volatile long value = 0;
}
}
- 未發生偽填充
public final class FalseSharing
implements Runnable
{
public final static int NUM_THREADS = 2; // change
public final static long ITERATIONS = 500 * 1000 * 1000;
private final int arrayIndex;
private static VolatileLong[] longs = new VolatileLong[NUM_THREADS];
static
{
for (int i = 0; i < longs.length; i++)
{
longs[i] = new VolatileLong();
}
}
public FalseSharing(final int arrayIndex)
{
this.arrayIndex = arrayIndex;
}
public static void main(final String[] args) throws Exception
{
final long start = System.nanoTime();
runTest();
System.out.println("duration = " + (System.nanoTime() - 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;
}
}
@sun.misc.Contended
public final static class VolatileLong
{
public volatile long value = 0;
}
}
其中需要加入 JVM 參數 - XX:-RestrictContended。
實驗結果
- 發生偽填充
- 未發生偽填充