版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xzknet/article/details/82630919
package com.csdn.blog.xzknet.test.flashsharing; public final class FalseSharingFail implements Runnable { public final static int NUM_THREADS = 4; // change public final static long ITERATIONS = 500L * 1000L * 1000L; 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 FalseSharingFail(final int arrayIndex) { this.arrayIndex = arrayIndex; } public static void main(final String[] args) throws Exception { System.out.println("VolatileLong未解决了伪共享问题.所以每次longs[i]都会将其他的longs[i+1]或者longs[i-1]的64位加载进来."); final long start = System.nanoTime(); runTest(); System.out.println("持续时间 = " + (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 FalseSharingFail(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 = 0L; //public long p1, p2, p3, p4, p5, p6; // comment out } }
解决方法很简单,就是把连续的内存块儿给搞一些无用的引用之类的放上去,例如long占8个字节object引用占4个字节,总之,可能会出现伪共享的字段后你就使劲加吧加够64个字节就是了.例如上面的问题改造方法见下面的代码.
package com.csdn.blog.xzknet.test.flashsharing; public final class FalseSharing implements Runnable { public final static int NUM_THREADS = 4; // change public final static long ITERATIONS = 500L * 1000L * 1000L; 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 { System.out.println("VolatileLong解决了伪共享问题."); final long start = System.nanoTime(); runTest(); System.out.println("持续时间 = " + (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 = 0L; public long p1, p2, p3, p4, p5, p6; // comment out } }
测试结果也给大家粘贴一下
VolatileLong未解决了伪共享问题.所以每次longs[i]都会将其他的longs[i+1]或者longs[i-1]的64位加载进来. 持续时间 = 45158255500
VolatileLong解决了伪共享问题. 持续时间 = 25888840119
差距还是很大的.尤其是在大并发的系统中,可能就是灾难性的.
解决伪共享的办法是使用缓存行填充,使一个对象占用的内存大小刚好为64bytes或它的整数倍,这样就保证了一个缓存行里不会有多个对象。《 剖析Disruptor:为什么会这么快?(三)伪共享 》提供了缓存行填充的例子:
public final static class VolatileLong { public volatile long value = 0L; public long p1, p2, p3, p4, p5, p6; // comment out }
因此,JAVA 7下做缓存行填充更麻烦了,需要使用继承的办法来避免填充被优化掉
public class VolatileLongPadding { public volatile long p1, p2, p3, p4, p5, p6; // 注释 }
public class VolatileLong extends VolatileLongPadding { public volatile long value = 0L; }
把padding放在基类里面,可以避免优化。(这好像没有什么道理好讲的,JAVA7的内存优化算法问题,能绕则绕)。
中添加了一个@Contended的注解
import sun.misc.Contended; @Contended public class VolatileLong { public volatile long value = 0L; }
执行时,必须加上虚拟机参数-XX:-RestrictContended,@Contended注释才会生效。