• 【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. }

    运行结果

  • 相关阅读:
    软件测试,作为职场新鸟?我该怎么办?看看资深5年测试的见解......
    .NET 使用 OpenTelemetry metrics 监控应用程序指标
    C++_第八周做题总结
    【微机原理笔记】第 4 章 - 8086 汇编语言程序设计
    装机必备的浏览器推荐,干净好用,选这4款不会出错
    ES6~ES13新特性(二)
    “再谈”协议
    宠物商城网站
    linux下如何hook第三方播放器的视频数据?
    李宏毅老师《机器学习》课程笔记-4.1 Self-attention
  • 原文地址:https://blog.csdn.net/m0_64921476/article/details/134480635