当前位置:网站首页>慢慢学JVM之缓存行和伪共享

慢慢学JVM之缓存行和伪共享

2022-06-26 05:17:00 青铜大神

缓存行(Cache Line)

        缓存行是CPU缓存的最小单位,CPU的缓存是由多个缓存行组成的,一个缓存行通常是64字节大小。所以一个缓存行可以存储8个long类型的变量。

        CPU缓存的工作流程是每次访问缓存,如果缓存命中,会把整个缓存行读取出来进行修改。这种机制就会产生伪共享问题。

伪共享问题

        伪共享问题产生在多线程环境下,线程A与线程B同时修改位于同一缓存行内的不同数据,导致缓存失效。它们会互相覆盖导致频繁的缓存未命中,

public class FalseShareTest implements Runnable {
    // 并发线程数:4
    public static int NUM_THREADS = 4;
    // 迭代次数:100万次
    public final static long ITERATIONS = 1_000_000L;
    // 数组索引数
    private final int arrayIndex;
    // VolatileLong对象数组
    private static VolatileLong[] longs;
    // 花费总时长
    public static long SUM_TIME = 0l;

    public FalseShareTest(final int arrayIndex) {
        this.arrayIndex = arrayIndex;
    }

    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 FalseShareTest(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 {
        // 加volatile让变量的修改对所有线程可见
        public volatile long value = 0L;
    }

    public static void main(final String[] args) throws Exception {
        // 执行10次
        for (int j = 0; j < 10; j++) {
            // 构建实验对象数组
            longs = new VolatileLong[NUM_THREADS];
            for (int i = 0; i < longs.length; i++) {
                longs[i] = new VolatileLong();
            }
            // 开始时间戳
            final long start = System.currentTimeMillis();
            // 运行测试程序
            runTest();
            // 结束时间戳
            final long end = System.currentTimeMillis();
            SUM_TIME += end - start;
        }
        System.out.println("总耗时:" + SUM_TIME);
    }
}

运行结果

如何改进?

        第一种办法,由于一个缓存行可以存储64个字节,也就是8个long型变量,那我就前后各安插7各long型变量,让字段value,前不着村,后不着店。这也是高性能队列Disruptoer的解决方式。

public final static class VolatileLong {

    // 填充
    public long p1, p2, p3, p4, p5;

    // 加volatile让变量的修改对所有线程可见
    public volatile long value = 0L;

    // 填充
    public long p6, p7, p8, p9, p10;
}

这里存在一个细节,一个long型变量在32位操作系统种占用8个字节,64位操作系统种占用16个字节,也就是说我偶们只需要5个long变量就能占满64个字节

        第二种办法,使用JDK8新增的@Contented,使用@Contented注解后会增加128字节的padding,需要启动时增加-XX:-RestrictContented选项才能生效。

原网站

版权声明
本文为[青铜大神]所创,转载请带上原文链接,感谢
https://blog.csdn.net/qq_22156459/article/details/125225555