• 【Java】volatile-内存可见性问题


    1、什么是内存可见性问题?

    (1)实例

    要明白什么是内存可见性,我们首先来看一段代码

    1. public class demo1 {
    2. public static int isQuit = 0;
    3. public static void main(String[] args) {
    4. Thread thread1 = new Thread(()->{
    5. while (isQuit == 0){
    6. }
    7. System.out.println("t1线程结束");
    8. });
    9. thread1.start();
    10. Thread thread2 = new Thread(()->{
    11. Scanner scanner = new Scanner(System.in);
    12. isQuit = scanner.nextInt();
    13. System.out.println("isQuit已被修改");
    14. });
    15. thread2.start();
    16. }
    17. }

    运行结果如下

    打开jconsole,查看thread1状态,发现thread1还在运行状态,也就是说thread1中的while循环还一直在继续

    为什么会这样呢?

    (2)内存可见性问题 

    计算机中,CPU在读取寄存器时比读取内存时快很多,这时编译器会对代码进行自动优化

    在上述代码中,由于while循环中没有代码,线程1不断地读取isQuit的值进行判断,操作过于频繁

    因此,编译器在第一次读取isQuit的值后,便将其存放在寄存器中,后续不再读取

    导致我们在线程2里对isQuit 的值进行了修改,线程1也不能察觉

    由此便产生了内存可见性问题

    2、volatile

    要解决内存可见性问题,我们首先想到的是使用Java中的关键字volatile

    在变量前加上关键字volatile修饰,变量便不会再被编译器优化,进而就不会产生内存可见性问题

    public volatile static int isQuit = 0;

    完整代码如下

    1. import java.util.Scanner;
    2. public class demo2 {
    3. public volatile static int isQuit = 0;
    4. public static void main(String[] args) {
    5. Thread thread1 = new Thread(()->{
    6. while (isQuit == 0){
    7. }
    8. System.out.println("t1线程结束");
    9. });
    10. thread1.start();
    11. Thread thread2 = new Thread(()->{
    12. Scanner scanner = new Scanner(System.in);
    13. isQuit = scanner.nextInt();
    14. System.out.println("isQuit已被修改");
    15. });
    16. thread2.start();
    17. }
    18. }

    最终运行结果

    可见,内存可见性问题可用volatile关键字来解决

    3、其他解决办法

     之所以产生内存可见性问题,是由于读取操作太过于频繁。只要我们降低读取的频率,同样也可以解决内存可见性问题

    如在线程1中的while循环中加上sleep操作

    1. import java.util.Scanner;
    2. public class demo3 {
    3. public static int isQuit = 0;
    4. public static void main(String[] args) {
    5. Thread thread1 = new Thread(()->{
    6. while (isQuit == 0){
    7. try {
    8. Thread.sleep(1000);
    9. } catch (InterruptedException e) {
    10. throw new RuntimeException(e);
    11. }
    12. }
    13. System.out.println("t1线程结束");
    14. });
    15. thread1.start();
    16. Thread thread2 = new Thread(()->{
    17. Scanner scanner = new Scanner(System.in);
    18. isQuit = scanner.nextInt();
    19. System.out.println("isQuit已被修改");
    20. });
    21. thread2.start();
    22. }
    23. }

    运行结果

  • 相关阅读:
    图搜索算法详解
    【C++】类的封装 ③ ( 访问控制权限 )
    uni-app之android离线打包
    俄罗斯方块
    JavaScript知识系列(3)每天10个小知识点
    Kettle连接Oracle(Oracle19c&Oracle11g)
    [机缘参悟-63]:《兵者,诡道也》-4-孙子兵法解读-攻战计
    Step2:Java内存区域与内存溢出异常
    通讯网关软件028——利用CommGate X2Modbus实现Modbus RTU访问PI服务器
    感统失调长大就好了?错,长大就晚了!
  • 原文地址:https://blog.csdn.net/m0_64921476/article/details/134480635