素手烹茶|探讨缓存行与伪共享( 二 )
可见 , 如果同一个 Cache line 的内容被多个线程读取 , 就会产生相互竞争 , 频繁回写主内存 , 降低了性能 。
如何解决伪共享问题要解决伪共享这个问题最简单的做法就是将线程间共享元素分开到不同的 Cache line 中 , 这种做法叫用空间换取时间 , 具体做法如下:
public final static class ValuePadding {// 前置填充对象protected long p1, p2, p3, p4, p5, p6, p7;// value 值protected volatile long value = http://kandian.youth.cn/index/0L;// 后置填充对象protected long p9, p10, p11, p12, p13, p14, p15;}JDK1.8 有专门的注解 @Contended 来避免伪共享 , 为了更加直观 , 我使用了对象填充的方法 , 其中 protected long p1, p2, p3, p4, p5, p6, p7 作为前置填充对象 , protected long p9, p10, p11, p12, p13, p14, p15作为后置填充对象 , 这样任意线程访问 ValuePadding 时 , value 都处于不同的 Cache line 中 , 不会产生伪共享问题 。
下面的例子用来演示伪共享与解决伪共享后的性能差异:
public class MyFalseSharing {public static void main(String[] args) throws InterruptedException {for (int i = 1; i < 10; i++) {System.gc();final long start = System.currentTimeMillis();runTest(Type.PADDING, i);System.out.println("[PADDING]Thread num " + i + " duration = " + (System.currentTimeMillis() - start));}for (int i = 1; i < 10; i++) {System.gc();final long start = System.currentTimeMillis();runTest(Type.NO_PADDING, i);System.out.println("[NO_PADDING] Thread num " + i + " duration = " + (System.currentTimeMillis() - start));}}private static void runTest(Type type, int NUM_THREADS) throws InterruptedException {Thread[] threads = new Thread[NUM_THREADS];switch (type) {case PADDING:DataPadding.longs = new ValuePadding[NUM_THREADS];for (int i = 0; i < DataPadding.longs.length; i++) {DataPadding.longs[i] = new ValuePadding();}break;case NO_PADDING:Data.longs = new ValueNoPadding[NUM_THREADS];for (int i = 0; i < Data.longs.length; i++) {Data.longs[i] = new ValueNoPadding();}break;}for (int i = 0; i < threads.length; i++) {threads[i] = new Thread(new FalseSharing(type, i));}for (Thread t : threads) {t.start();}for (Thread t : threads) {t.join();}}// 线程执行单元static class FalseSharing implements Runnable {public final static long ITERATIONS = 500L * 1000L * 100L;private int arrayIndex;private Type type;public FalseSharing(Type type, final int arrayIndex) {this.arrayIndex = arrayIndex;this.type = type;}public void run() {long i = ITERATIONS + 1;// 读取共享变量中指定的下标对象 , 并对其value变量不断修改// 由于每次读取数据都会写入缓存行 , 如果线程间有共享的缓存行数据 , 就会导致伪共享问题发生// 如果对象已填充 , 那么线程每次读取到缓存行中的对象就不会产生伪共享问题switch (type) {case NO_PADDING:while (0 != --i) {Data.longs[arrayIndex].value = http://kandian.youth.cn/index/0L;}break;case PADDING:while (0 != --i) {DataPadding.longs[arrayIndex].value = 0L;}break;}}}// 线程间贡献的数据public final static class Data {public static ValueNoPadding[] longs;}public final static class DataPadding {public static ValuePadding[] longs;}// 使用填充对象public final static class ValuePadding {// 前置填充对象protected long p1, p2, p3, p4, p5, p6;// value 值protected volatile long value = 0L;// 后置填充对象protected long p9, p10, p11, p12, p13, p14, p15;}// 不填充对象//@sun.misc.Contendedpublic final static class ValueNoPadding {protected volatile long value = 0L;}enum Type {NO_PADDING,PADDING}}
推荐阅读
- 素手烹茶|Redis 事务也叫事务吗?,不支持原子性的
- 素手烹茶|中芯或被“拉黑”要求退出群聊,这是机遇还是无奈?
- 素手烹茶|尘封23年的运钞车劫案告破:嫌犯在部队服役时作案,转业后两获三等功成法官
- 素手烹茶|你知道马云被救了3次吗?
- 车驰夜幕|联合国向你发来活动邀请:与全球尖端人士探讨关注自然的真谛
- 新华网|适用率高 上诉率低——法律界肯定认罪认罚从宽制度成效 探讨制度完善
- 素手烹茶|我的电商故事:新手投资25万开天猫店,半年时间赚了100w
- 素手烹茶|- 高性能低损耗的 Goroutine 池,Ants
- 素手烹茶|腾讯微博将于9月28日停止运营;阿里国内消费者已接近10亿;信通院:5年里5G商用可直接带动信息消费8.2万亿元|Do早报
- 电子商务|除了做电商发家,年轻人还可以有哪些职业发展方向,一起来探讨
