CPU缓存问题及volatile的使用

1 现象

/**
* @Description: 写线程更新INIT_VALUE的值,读线程打印INIT_VALUE更新后的值
* @Auther: zhurongsheng
* @Date: 2020/3/30 12:55
*/
public class VolatileTest {
    private  volatile static int INIT_VALUE = 0;
    private final static int MAX_LIMIT = 50;
    public static void main(String[] args) {
        /**
         *  当INIT_VALUE更新时,打印INIT_VALUE的值
         */
        new Thread(() -> {
            int localValue = INIT_VALUE;
            while (localValue < MAX_LIMIT) {
                if (localValue != INIT_VALUE) {
                    System.out.println("The value updated to " + INIT_VALUE);
                    localValue=INIT_VALUE;
                }
            }
        }, "READER").start();
        /**
         * 每隔0.5秒 增加 INIT_VALUE
         */
        new Thread(() -> {
            int localValue = INIT_VALUE;
            while (INIT_VALUE < MAX_LIMIT) {
                System.out.println("Update the  value  to " + ++localValue);
                INIT_VALUE=localValue;
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "UPDATE").start();
    }
}

在这里插入图片描述

2 CPU缓存问题

2.1数据不一致问题

UPDATE线程和READER各自玩各自的Cache,所以UPDATE线程更新INIT_VALUE时更新的是CACHE2的数据,READER是读取不到的

在这里插入图片描述

2.2 两个线程t1,t2同时对INIT_VALUE进行操作,由于各自玩各自的缓存,是否能打印出两个两个线程的0-10

/**
* @Description: 两个线程t1,t2同时对INIT_VALUE进行操作,由于各自玩各自的缓存,是否能打印出两个两个线程的0-10
* @Auther: zhurongsheng
* @Date: 2020/3/30 12:55
*/
public class VolatileTest2 {
    private static int INIT_VALUE = 0;
    private final static int MAX_LIMIT = 10;
    public static void main(String[] args) {


        new Thread(() -> {
            int localValue = INIT_VALUE;
            while (INIT_VALUE < MAX_LIMIT) {
                System.out.println("t1 the  value  to " + ++localValue);
                INIT_VALUE = localValue;
            }
        }, "t1").start();


        new Thread(() -> {
            int localValue = INIT_VALUE;
            while (INIT_VALUE < MAX_LIMIT) {
                System.out.println("t2 the  value  to " + ++localValue);
                INIT_VALUE = localValue;
            }
        }, "t2").start();
    }
}

在这里插入图片描述
在这里插入图片描述

2.3 解决方案

(1) 给数据总线加锁
CPU跟其它硬件通讯方式都是通过总线的,总线又分(数据、地址、控制),当操作main memory数据时,对该部分的数据总线加锁。这样能解决问题,但是多核会串行化,导致计算速度慢。

(2)CPU高速缓存一致性协议
当CPU写入数据的时候,如果发现该变量是共享变量(在其它CPU中也存在该变量的副本),会发出一个信号,通知其他CPU该变量的缓存无效了。

3 并发编程的三个重要概念

3.1 原子性

原子性:即一个操作或者多个操作要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

3.2 可见性

可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。

3.3 有序性

有序性:即程序执行的顺序按照代码的先后顺序执行。

(1) 代码

int i = 0;
boolean flag = false;
//语句1
i = 1;
//语句2
flag = true;

(2) 重排序(最终一致性)
上面代码定义了一个int型变量,定义了一个boolean类型变量,然后分别对两个变量进行赋值操作。从代码顺序上看,语句1是在语句2前面的,那么JVM在真正执行这段代码的时候会保证语句1一定会在语句2前面执行吗?不一定,为什么呢?这里可能会发生指令重排序(Instruction Reorder)。重排序,一般来说,处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。

5 volatile

5.1 volatile

(1) 保证了不同线程间的可见性,如果是写操作,强制把缓存的数据写入主存,使其它CPU缓存中的数据失效
(2) 禁止对其进行重排序,保证有序性
(3) 并未保证原子性

扫描二维码关注公众号,回复: 11444495 查看本文章

5.2 可见性

/**
* @Description: 可见性测试
* @Auther: zhurongsheng
* @Date: 2020/3/30 12:55
*/
public class VolatileTest1 {
    private static volatile int INIT_VALUE = 0;
    private final static int MAX_LIMIT = 50;
    public static void main(String[] args) {
        /**
         *  当INIT_VALUE更新时,打印INIT_VALUE的值
         */
        new Thread(() -> {
            int localValue = INIT_VALUE;
            while (localValue < MAX_LIMIT) {
                if (localValue != INIT_VALUE) {
                    System.out.println("The value updated to " + INIT_VALUE);
                    localValue = INIT_VALUE;
                }
            }
        }, "READER").start();
        /**
         * 每隔0.5秒 增加 INIT_VALUE
         */
        new Thread(() -> {
            int localValue = INIT_VALUE;
            while (INIT_VALUE < MAX_LIMIT) {
                System.out.println("Update the  value  to " + ++localValue);
                INIT_VALUE = localValue;
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "UPDATE").start();
    }
}

在这里插入图片描述

2.3 原子性测试

/**
* @Description: 两个线程同时对INIT_VALUE进行累加操作看是否是协同操作
* @Auther: zhurongsheng
* @Date: 2020/3/31 22:26
*/
public class VolatileTest3 {
    private volatile static int INIT_VALUE = 0;
    private final static int MAX_LIMIT = 10;
    public static void main(String[] args) {
        new Thread(() -> {
            int localValue = INIT_VALUE;
            while (INIT_VALUE < MAX_LIMIT) {
                System.out.println("t1 the  value  to " + ++localValue);
                INIT_VALUE = localValue;
            }
        }, "t1").start();
         
        new Thread(() -> {
            int localValue = INIT_VALUE;
            while (INIT_VALUE < MAX_LIMIT) {
                System.out.println("t2 the  value  to " + ++localValue);
                INIT_VALUE = localValue;
            }
        }, "t2").start();
    }
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_34125999/article/details/105239131