美文网首页
线程安全可见性问题

线程安全可见性问题

作者: 佐蓝Gogoing | 来源:发表于2019-05-19 16:09 被阅读0次
public class VisibilityDemo {

    private boolean flag = true;

    public static void main(String[] args) throws InterruptedException {
        VisibilityDemo demo = new VisibilityDemo();
        System.out.println("开始运行");
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                int i = 0;
                while (demo.flag){
                    i++;
                }
                System.out.println(i);
            }
        });
        thread1.start();
        TimeUnit.SECONDS.sleep(2);
        demo.flag = false;
        System.out.println("运行结束");
    }
}

如上,按照这段代码的目的来说,会在两秒后打印出 i 的值并结束。但运行后并不会结束,如下图



这就是一个可见性问题

那么问题就来了

  1. 这段程序为什么不会正常运行
  2. 如何能够让它正常运行

为什么不会正常运行

  1. 缓存导致的可见性问题
    每一个线程都有自己的本地变量,从内存中拷贝到CPU缓存,每个CPU核心有自己的缓存。但是CPU自身保证了缓存的一致性,缓存刷新会有延迟,但也是延迟一段时间 i 就会打印出来,不会一直不打印。

  2. JVM 的指令重排序
    JVM 的运行模式分为三种:编译模式、解释模式、混合模式
    编译模式:jit 将字节码提前编译为汇编
    解释模式:在程序运行时,一行一行的将字节码编译为汇编
    混合模式:运行过程中,对热点代码进行编译优化,与编译模式相同,其他非热点与解释模式相同
    在这个例子中,由于 jit 的优化导致程序没有按照预想的运行,优化后的程序相当于以下伪代码:

public void run() {
    int i = 0;
    boolean flag = demo.flag;
    while (flag){
        i++;
    }
    System.out.println(i);
}

指令重排序是在汇编阶段的操作,无法通过代码直接看到,不过我们可以在 jvm 的启动参数上加上

-Djava.compiler=NONE

关闭自动优化来验证


可以看到关闭后就打印出了 i 的值

如何修改代码使其正常运行

刚刚通过关闭 jit 的优化使其正常运行了,还可以通过修改代码让它正常运行

  1. volatile
private volatile boolean flag = true;

jvm 规范规定了,被 volatile 关键字修饰的变量不能够进行指令重排序,也不能被CPU缓存,修改的值实时地写入内存。在这里 volatile 可以解决这个问题。
注意:volatile 是语法层面的,并不是 volatile 直接起作用,而是 jvm 规范规定了,然后厂商按照规范开发虚拟机,是在虚拟机层面实现了 volatile 涉及的功能。

  1. synchronized

synchronized 关键字在这里不能解决这个问题,锁可以确保同一时间只有一个线程操作 i 变量,但这里不是多线程导致的,所以不能解决这个问题。

  1. lock

同 synchronized 也不能解决这个问题。

相关文章

  • 并发编程-安全性、活跃性以及性能问题

    一、安全性问题 并发 Bug 的三个主要源头:原子性问题、可见性问题和有序性问题。理论上线程安全的程序,就要避免出...

  • 线程安全可见性问题

    如上,按照这段代码的目的来说,会在两秒后打印出 i 的值并结束。但运行后并不会结束,如下图 这就是一个可见性问题 ...

  • 线程安全之可见性问题

    Java内存模型 VS JVM运行时数据区 首先Java内存模型(JMM)和JVM运行时数据区并不是一个东西,许多...

  • Java线程安全-可见性问题

    Java内存模型(JMM) 与 JVM运行时数据区 Java内存模型是《Java语言规范》中,描述对java语...

  • 02.线程安全性问题

    [TOC] 安全性问题概述 什么是安全性问题 多线程情况下的安全问题,是指数据的一致性问题,在多线程环境下,多个线...

  • 互斥锁,解决原子性问题

    并发编程有3个源头性问题:缓存导致的可见性问题,编译优化导致的有序性问题,以及线程切换导致的原子性问题。解决可见性...

  • 1.2.1 线程安全之可见性问题

    多线程中的问题 所见非所得 无法内眼去检查程序的准确性 不同的运行平台有不同的表现 错误很难重现 工作内存缓存 内...

  • Java并发编程学习笔记(一)

    本文来源:王宝令——《Java并发编程实战》 导致线程安全问题的原因 源头之一:缓存导致的可见性问题 一个线程对共...

  • 第2章 并发编程的其他基础知识

    目录 并行与并发区别 Java中的线程安全问题 Java中共享变量的内存可见性问题 synchronized关键字...

  • synchronized关键字

    线程安全的主要来源就是JMM的设计,主要集中在主内存和线程工作内存而导致的内存可见性问题以及重排序导致的问题。ja...

网友评论

      本文标题:线程安全可见性问题

      本文链接:https://www.haomeiwen.com/subject/vpwxzqtx.html