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

    运行结果

  • 相关阅读:
    Verilog中 高位与低位
    linux检测系统是否被入侵(完整篇)
    java毕业设计教务管理系统(附源码、数据库)
    便宜质量又好的学生蓝牙耳机有哪些?内行推荐四款便宜好用的蓝牙耳机
    怎么减少行锁对性能的影响?
    亚马逊关键词搜索API接口(item_search-按关键字搜索亚马逊商品接口),亚马逊API接口
    电商项目中订单系统到底该怎么设计才好?(至尊典藏版)
    Kotlin Flow响应式编程,操作符函数进阶
    【无标题】
    GCC + Vscode 搭建 nRF52xxx 开发环境
  • 原文地址:https://blog.csdn.net/m0_64921476/article/details/134480635