假设 计算单元 要找变量X,需要从最近的缓存中找L1 --> L2 --> L3 --> 主内存。
如果是要读取一个变量则顺序为:主内存 -> L3 -> L2 -> L1,之后 计算单元 开始计算。
当我们读一个数据,会把相关的数据一块读过来(即 按块读取),
class B {
private static class T {
public volatile long x = 0L; // long 类型 8字节
}
public static T[] arr = new T[2];
static {
arr[0] = new T();
arr[1] = new T();
}
/**
* 在内存中有2块数据arr[0].x 记为x、arr[1].x 记为y,它们在同一个缓存行中。
* 在CPU层面的数据一致性,是以cache line为单位(64字节)的。所以当同一个缓存
* 行中的某个值发生了变化(volatile保证可见性),需要通知另一个线程去拉取最新值。
**/
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
for (long i = 0; i < 1000_0000L; i++){
// 线程t1 修改x的之后,每次都会通知线程t2拉取最新值
arr[0].x = i;
}
});
Thread t2 = new Thread(() -> {
for (long i = 0; i < 1000_0000L; i++){
arr[1].x = i;
}
});
final long start = System.nanoTime();
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println((System.nanoTime() - start) / 100_0000);
}
}
优化
class B1 {
private static class Padding {
// long 类型 8字节 共占用56个字节
public volatile long p1,p2,p3,p4,p5,p6,p7;
}
private static class T extends Padding {
// 56 + 8 = 64
public volatile long x = 0L;
}
public static T[] arr = new T[2];
static {
arr[0] = new T();
arr[1] = new T();
}
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
for (long i = 0; i < 1000_0000L; i++){
// 线程t1 的x变量,与线程t2 的x变量不在同一个 缓存行中,所以每次修改都不用通知
// t2,这样就提高了效率。
arr[0].x = i;
}
});
Thread t2 = new Thread(() -> {
for (long i = 0; i < 1000_0000L; i++){
arr[1].x = i;
}
});
final long start = System.nanoTime();
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println((System.nanoTime() - start) / 100_0000);
}
}