美文网首页
JMM模型与volatile关键字

JMM模型与volatile关键字

作者: 小方块886 | 来源:发表于2021-04-03 23:30 被阅读0次

1.理解什么是JMM

JMM是Java内存模型,不同于JVM的。JVM是程序执行实际存在的工作空间,而JMM是java内存模型是抽象的概念,并不是真是存在,它主要是在java并发编程或者说是多线程下提出的一个描述。
JMM是围绕可见性,原子性,有序性展开

2.理解java工作内存

Snipaste_2021-04-03_22-43-48.png

每个线程会有自己独有的工作内存,将主内存共享变量读取一份副本在工作内存中
执行顺序大概分成3步:1.读取变量-2.逻辑运算-3.写入主内存
按照上面3个步骤其实我们可以发现,如果实在高并发场景下,线程1对变量修改时候,还没有将修改后的变量写入主内存,那么此时线程二进来读取到旧变量就会有问题。
上代码演示

   private static boolean init = false;

    public static void main(String[] args) throws Exception {
        new Thread( () -> {
            System.out.println("线程1begin");
            while (!init){
            }
            System.out.println("线程1end");
        } ).start();

        Thread.sleep(2000);
        new Thread(() -> {
            System.out.println("线程2begin");
            init = true;
            System.out.println("线程2end");
        }).start();

    }

代码是在main方法中开启了两个线程,线程一获取init变量,如果init为false取反后将会死循环,线程二将init变量更新为true。
思考:“线程1end”这句话会不会打印?
结论:不会,因为JMM线程模型中,提到了每个线程会有自己的内存缓存,线程1的init值是false

3.volatile关键字

被volatile修饰的关键字会让变量具有JMM内存模型中提到的可见性,可见性可以理解为线程获取变量的时候是每次都从主内存获取,修改变量的时间会马上同步到主内存,这样就保证了其他线程获取到的变量是及时可见的。
改造上面代码,将变量用volatile修饰

    private volatile static boolean init = false;

    public static void main(String[] args) throws Exception {
        new Thread( () -> {
            System.out.println("线程1begin");
            while (!init){
            }
            System.out.println("线程1end");
        } ).start();

        Thread.sleep(2000);
        new Thread(() -> {
            System.out.println("线程2begin");
            init = true;
            System.out.println("线程2end");
        }).start();

    }

思考:“线程1end”这句话会不会打印?
结论:会打印,因为此时init变量是可见性的变量,线程1会直接从主内存中获取到init的值

4. 深入理解JMM内存模型,颠覆认知

从上面的示例中我们已经理解了volatile关键字以及线程的内存模型,但是其实没有这么简单,下面会用一下代码来做演示

    private static boolean init = false;
    // 注意:count使用int类型
    private static int count = 0;

    public static void main(String[] args) throws Exception {
        new Thread( () -> {
            System.out.println("线程1begin");
            while (!init){
                // 做一个变量自增操作
                count++;
            }
            System.out.println("线程1end");
        } ).start();

        Thread.sleep(2000);
        new Thread(() -> {
            System.out.println("线程2begin");
            init = true;
            System.out.println("线程2end");
        }).start();

    }

思考:“线程1end”这句话会不会打印?
结论:不会
结论是不会打印,那么我们再来看看下面的代码,下面代码我把count的int类型换成Integer

    private static boolean init = false;
    private static Integer count = 0;

    public static void main(String[] args) throws Exception {
        new Thread( () -> {
            System.out.println("线程1begin");
            while (!init){
                // 做一个变量自增操作
                count++;
            }
            System.out.println("线程1end");
        } ).start();

        Thread.sleep(2000);
        new Thread(() -> {
            System.out.println("线程2begin");
            init = true;
            System.out.println("线程2end");
        }).start();

    }

思考:“线程1end”这句话会不会打印?
结论:会
看到这里应该很多人都不知道为啥了,会问为什么,或者不相信,下面我贴出这段代码运行后的结果在往后面说

线程1begin
线程2begin
线程2end
线程1end

很明显“线程1end”已经被打印了,看到这里其实我也不知道是为啥,因为情况很多,目前我也没找到解释这种现象的原因,很多种情况比如下面我继续试验:

    private static boolean init = false;
    private volatile static int count = 0;

    public static void main(String[] args) throws Exception {
        new Thread( () -> {
            System.out.println("线程1begin");
            while (!init){
                // 做一个变量自增操作
                count++;
            }
            System.out.println("线程1end");
        } ).start();

        Thread.sleep(2000);
        new Thread(() -> {
            System.out.println("线程2begin");
            init = true;
            System.out.println("线程2end");
        }).start();

    }

思考:count还是int类型,但是被我加了volatile关键字,“线程1end”这句话会不会打印?
结论:会打印
继试验了很多情况如:在while里面休眠,或者在while做一些业务逻辑操作等等,都是可以成功打印“线程1end”的

volatile及时可见性

可能现在还没办法解释上面的现象,但是我们理解的volatile关键字,是可以使我们的变量具有可见性,如果没有volatile关键字,也并不一定说不同线程就是一定在自己的副本中操作变量,永远不会从主内存中更新变量,只能说volatile关键字,为我们提供了在多线程和并发变成下,为我们的变量提供了及时的可见性,变量的可见性也是线程安全的3要素之一

线程安全需要保证3个关键点:可见性,原子性,有序性

可见性:线程直接从主内存中获取共享变量
原子性:同时成功或者同时失败
有序性:代码顺序从上往下执行,但是计算机有可能会对代码进行指令重排,会让结果出现意外情况

笔记

原子性平时我们见的比较多就不多说了,volatile可以实现可见性的同时也是可以实现有序性的,有序性还需要继续深入理解学习,这里可能没有笔记,以后会在学习的过程中慢慢补上的,学习永无止境,希望可以一直遨游在知识的海洋啦~

相关文章

  • (转载)全面理解Java内存模型(JMM)及volatile关键

    原文链接:全面理解Java内存模型(JMM)及volatile关键字 - CSDN博客 理解Java内存区域与Ja...

  • JMM模型与volatile关键字

    1.理解什么是JMM JMM是Java内存模型,不同于JVM的。JVM是程序执行实际存在的工作空间,而JMM是ja...

  • 1.4 volatile 关键字

    volatile 关键字 volatile关键字是与Java的内存模型有关的,因此需要先了解一下与内存模型相关的概...

  • 美团面试官:小伙子,说一下volatile关键字原理吧?

    前言 在讲述Volatile关键字之前,我们先大概讲一下cpu多核并发缓存架构,再到JMM,即java内存模型,最...

  • java学习笔记

    1.volatile 2.JMM(java内存模型) 3.volatile代码可见性 4.volatile代码不保...

  • java内存模型

    主要看懂这篇文章全面理解Java内存模型(JMM)及volatile关键字。 理解三张图 内存区域图 方法区(静态...

  • volatile关键字原理吧

    在讲述Volatile关键字之前,我们先大概讲一下cpu多核并发缓存架构,再到JMM,即java内存模型,最后到v...

  • java并发编程实战

    并发编程之原子性、可见性和有序性 Volatile关键字:因为Java内存模型(JMM)即每个线程都会有一份本地缓...

  • java初入多线程5

    volatile 与java内存模型(JMM) java的内存模型都是围绕着原子性、有序性、还有可见性来展开的。 ...

  • JMM之happens-before

    happens-before是JMM的核心概念,也是理解volatile关键字的基础,如果想彻底掌握JMM和并发编...

网友评论

      本文标题:JMM模型与volatile关键字

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