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中这样使用。