【java】java 如何证明linux缓存行确实存在

在这里插入图片描述

1.概述

在这里插入图片描述
三级缓存的结构
在这里插入图片描述
在三级缓存中,L1,L2位于每个cpu核之中,L3缓存是位于CPU中的是多核共享的(一个CPU内)
在这里插入图片描述
为什么要加入缓存呢?
在这里插入图片描述
因为CPU和内存的速度不匹配,约等于 100:1

这里面有时间局部性与空间局部性,这连个概念请自寻百度。

在这里插入图片描述
这个图是空间局部性,读取x的时候,y也可能在下一次读取。

而且一个cpu操作了x后,要通知另外一个cpu来重新读取,这涉及缓存一致性。

目前缓存行大小是 64个字节。

2.证明

package com.java.memory.memoryline;

import java.util.concurrent.CountDownLatch;

/**
 * @author: chuanchuan.lcc
 * @date: 2020-12-20 16:26
 * @modifiedBy: chuanchuan.lcc
 * @version: 1.0
 * @description:
 */
public class MemoryLineDemo1 {
    
    

    private static long COUNT = 100000000L;

    private static class T1 {
    
    
        private volatile long x = 0L;
    }


    public static T1[] arr = new T1[2];

    static {
    
    
        arr[0] = new T1() ;
        arr[1] = new T1();
    }

    //耗时2843
    public static void main(String[] args) throws InterruptedException {
    
    
        CountDownLatch latch = new CountDownLatch(2);
        Thread t1 = new Thread(() -> {
    
    
            for (long i = 0; i < COUNT; i++) {
    
    
                arr[0].x = i;
            }
            latch.countDown();
        });

        Thread t2 = new Thread(() -> {
    
    
            for (long i = 0; i < COUNT; i++) {
    
    
                arr[1].x = i;
            }
            latch.countDown();
        });

        final long start = System.nanoTime();
        t1.start();
        t2.start();
        latch.await();

        final long end = System.nanoTime();
        System.out.println("耗时" + (end - start)/1000000);
    }


}

这个程序执行,大概耗时2843,然后将这个程序改成如下

package com.java.memory.memoryline;

import java.util.concurrent.CountDownLatch;

/**
 * @author: chuanchuan.lcc
 * @date: 2020-12-20 16:31
 * @modifiedBy: chuanchuan.lcc
 * @version: 1.0
 * @description:
 */
public class MemoryLineDemo2 {
    
    


    private static long COUNT = 100000000L;

    private static class T1 {
    
    
        private long p1, p2, p3, p4, p5, p6, p7;
        private volatile long x = 0L;
        private long p8, p9, p10, p11, p12, p13, p14;
    }



    public static T1[] arr = new T1[2];

    static {
    
    
        arr[0] = new T1() ;
        arr[1] = new T1();
    }

    // 耗时1093
    public static void main(String[] args) throws InterruptedException {
    
    
        CountDownLatch latch = new CountDownLatch(2);
        Thread t1 = new Thread(() -> {
    
    
            for (long i = 0; i < COUNT; i++) {
    
    
                arr[0].x = i;
            }
            latch.countDown();
        });

        Thread t2 = new Thread(() -> {
    
    
            for (long i = 0; i < COUNT; i++) {
    
    
                arr[1].x = i;
            }
            latch.countDown();
        });

        final long start = System.nanoTime();
        t1.start();
        t2.start();
        latch.await();

        final long end = System.nanoTime();
        System.out.println("耗时" + (end - start)/1000000);
    }
}

个程序执行,大概耗时1093,这俩程序不同点就是T1对象

3.解析

在第一个程序中,因为long 是8个字节,而 new T1[2]的时候数组内存是连续的,因此arr[0] arr[1] 是两个内存相连的内存,但是因为long 是8个字节,而缓存行一般为64字节,因为这两个参数很可能在同一个缓存块里,因此可能每个cpu核都缓存了两个值,然后每个修改因为有volatile,所以会互相通知修改,这里比较耗时。

而第二个程序,因为long 是8个字节,而 new T1[2]的时候数组内存是连续的,因此arr[0] arr[1] 是两个内存相连的内存,但是因为long 是8个字节,而缓存行一般为64字节,但是在x之前p1-p7这就是7*8=56个字节,然后是x,然后后面又是56个字节。

56字节
x 8字节
56字节

因为缓存行是64,然后x和前后组合都是64个字节,因此两个X不可能在一个缓存行中,因此两个线程修改的时候,不需要互相通知,因此不会耗时。

4.案例

真有这样使用的吗?有的,有个框架叫dispature,意思是闪电的意思
看其中的源码

在这里插入图片描述
这里就是7个long的值不使用。前面也是7个long

在这里插入图片描述
这个也是在currentHashMap中这样使用。

猜你喜欢

转载自blog.csdn.net/qq_21383435/article/details/111461020
今日推荐