Loading

多线程——JMM多线程共享变量的不可见性

多线程——JMM多线程共享变量的不可见性


示例1:

public class Demo1 {
    private static boolean flag = false;

    public static void main(String[] args) {
        // 线程1 读flag的值
        new Thread(() -> {
            while (true) {
                if (flag) {
                    System.out.println("线程1 读 flag=" + flag);
                    System.exit(0);
                }
            }
        }).start();
        
// 注释1:等一会线程1,至少把主内存中flag的值复制到自身的工作内存
//        try {
//            Thread.sleep(1);// 等待时间大一点,线程1的工作内存即可初始化完成
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        
        // 线程2 写flag的值
        new Thread(() -> {
            while (true) {
                flag = true;
            }
        }).start();
    }
}

不打开注释1,结果

  • 线程1 读 flag=true

打开注释1,结果有两种

  • 1.打印main sleep 之后陷入死循环
  • 2.打印main sleep 线程1 读 flag=true

打开注释1的第二种结果比较少见,这是因为线程1还没把主内存中的flag复制到工作内存中。

​ 根据JMM模型,显然在打开注释1出现的第一种结果是符合预期的。线程1从主内存复制了最初的一份flag=false到自己的工作内存中,之后主内存中的flag=true,并没有更新到线程1的工作内存。因此线程1看到的flag一直是最初的false。

示例2:

public class Demo2 {
    private static boolean flag = true;
    public static void main(String[] args) {
        new Thread(()->{
            while (flag){
// 注释1:                
//                System.out.println("over");
            }
        }).start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        flag=false;
    }
}

不打开注释1,结果

  • 陷入死循环

打开注释1,结果

  • 输入多次 over 然后正常退出

不打开注释1的结果是符合预期的,线程1的工作内存中的flag一直是最初从主内存拷贝的值true,所以陷入死循环。

打开注释1的结果,预想是一直循环输入over,但是程序会在输出一定次数后正常退出。这是为什么呢?这是由于 打印语句触发了happens-before操作。

JMM多线程共享变量的不可见性该如何解决呢?一、加,二、部分场景下可以使用volatile关键字。

参考:

1.多线程的共享变量的内存不可见性如何理解

2.java并发编程(十六)happen-before规则

posted @ 2020-11-15 22:00  齐玉  阅读(192)  评论(0)    收藏  举报