image.png
    在CPU中,磁盘读取内存中的数据是按块读取的。
    ALU读取X的时候,会先找L1 Cache中有无数据,有则返回,无则找L2中有无数据,然后再找L3中,最后才查找Main Memory中。在Main Memory中读取的时候是会把X所在的这一个内存块一起读取出来的,每个内存块占64个字节,读取到数据后,会依次更新L3,L2,L1缓存,然后再返回给磁盘。

    1. public class T01_CacheLineDemo {
    2. private static class T{
    3. public volatile long x = 0L;
    4. }
    5. public static T[] arr = new T[2];
    6. static {
    7. arr[0] = new T();
    8. arr[1] = new T();
    9. }
    10. public static void main(String[] args) throws InterruptedException {
    11. Thread t1 = new Thread(() -> {
    12. for (long i = 0; i < 1000_0000L; i++) {
    13. arr[0].x = i;
    14. }
    15. });
    16. Thread t2 = new Thread(() -> {
    17. for (long i = 0; i < 1000_0000L; i++) {
    18. arr[1].x = i;
    19. }
    20. });
    21. final long start = System.nanoTime();
    22. t1.start();t2.start();
    23. t1.join();t2.join();
    24. System.out.println((System.nanoTime() - start) / 100_0000);
    25. }
    26. }

    运行结果大约在300ms左右

    在第一个程序中,t1线程循环1000w次修改arr[0].x就要通知1000w次t2线程来保证内存一致性,因为arr[0]对象个arr[1]对象时在同一个内存块中。

    1. public class T02_CacheLineDemo {
    2. private static class Padding{
    3. public volatile long p1,p2,p3,p4,p5,p6,p7;
    4. }
    5. private static class T extends Padding{
    6. public volatile long x = 0L;
    7. }
    8. public static T[] arr = new T[2];
    9. static {
    10. arr[0] = new T();
    11. arr[1] = new T();
    12. }
    13. public static void main(String[] args) throws InterruptedException {
    14. Thread t1 = new Thread(() -> {
    15. for (long i = 0; i < 1000_0000L; i++) {
    16. arr[0].x = i;
    17. }
    18. });
    19. Thread t2 = new Thread(() -> {
    20. for (long i = 0; i < 1000_0000L; i++) {
    21. arr[1].x = i;
    22. }
    23. });
    24. final long start = System.nanoTime();
    25. t1.start();t2.start();
    26. t1.join();t2.join();
    27. System.out.println((System.nanoTime() - start) / 100_0000);
    28. }
    29. }

    运行时间大约在60ms左右

    在第二个程序中,因为父类Padding中有7个long类型的属性,我们都知道long类型占8个字节,所有7个属性就占有8*7=56个字节。在子类T中有个long类型的x占8个字节,所有56+8=64个字节,这说明一个T对象就占有一个内存块,不会和其他的对象在同一个内存块中,不需要考虑一致性问题。