Java内存模型(JMM)和volatile原理

这篇具有很好参考价值的文章主要介绍了Java内存模型(JMM)和volatile原理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一、Java 内存模型

二、可见性

三、有序性

四、volatile原理

 1、可见性保证

2、有序性保证

五、线程安全的单例


一、Java 内存模型

JMM即Java Memory Model,他定义了主存(共享的数据)工作内存(私有的数据)抽象概念,底层对应着CPU寄存器、缓存、硬件内存、CPU指令优化等

JMM体现以下几个方面

  • 原子性-保证指令不会受到线程上下文切换的影响
  • 可见性-保证指令不会受cpu缓存的影响
  • 有序性-保证指令不会受cpu指令并行优化的影响

二、可见性

现在有个run共享变量为true,主线程在sleep,子线程在while循环条件为run为true,然后主线程醒了就run变false,但是我们发现子线程还在运行。

Java内存模型(JMM)和volatile原理

因为Java把内存分为了主存和共享内存

初始状态,t1线程刚开始从主存读取了run的值到工作内存,

Java内存模型(JMM)和volatile原理

因为t要频繁的从出内存中读取run的值,JIT编译器会将run的值缓存到自己工作内存中的高速缓存(工作内存的底层关联了cpu缓存),减少对主存中run的访问,提高效率

Java内存模型(JMM)和volatile原理

1秒后main改了run但是t1线程是从自己的缓存中拿的,所以不知道

Java内存模型(JMM)和volatile原理

解决方案:

volatile(易变关键字)

用来修饰成员变量静态成员变量(局部不可以),就表示这个是变量是从主存中拿取,避免现场从自己工作缓存中查找变量的值,必须到主存中获取,线程操作volatile变量就是直接操作主存

synchronized也可以解决,但是要上monitor操作系统的锁,性能更差

Java内存模型(JMM)和volatile原理

其实在死循环中加入System.out.println也能让停止

    private void newLine() {
        try {
            synchronized (this) {
                ensureOpen();
                textOut.newLine();
                textOut.flushBuffer();
                charOut.flushBuffer();
                if (autoFlush)
                    out.flush();
            }
        }
        catch (InterruptedIOException x) {
            Thread.currentThread().interrupt();
        }
        catch (IOException x) {
            trouble = true;
        }
    }

因为print方法加了synchronized关键字(当然println也加了,换行操作也会持有锁)
而synchronized方法也是能保证了该同步块中的变量的可见性的,所以下次stop从主存中读出false就跳出了while。
这里记录一下synchronized做的操作:
1、获得同步锁;
2、清空工作内存;
3、从主内存拷贝对象副本到工作内存;
4、执行代码(计算或者输出等);
5、刷新主内存数据;
6、释放同步锁。
总结一句话:sychronized可以保证变量的可见性

可见性和原子性

前面的例子就是可见性,保证多个线程之间,一个线程对volatile变量的修改对另一个线程可见,不能保证原子性,只有一个线程写,多个线程读的时候,这个情况很适合volatile。

synchronized既可以保证原子性,也可以保证可见性。但缺点是synchronized重量级操作,性能相对比较低。

三、有序性

JVM会在不影响正确性的前提下调整语句的执行顺序,这种JVM自己调整执行顺序的操作叫指令重排。但在多线程下指令重拍可能会影响正确性。

指令重排在多线程下可能存在问题

Java内存模型(JMM)和volatile原理

比如上面这种情况,线程2是给num赋值,然后ready为true就唤醒线程1,这个时候如果指令重排了,先执行换标记,然后唤醒了,最后才赋值就会出问题了,他就会直接用0的值

 Java内存模型(JMM)和volatile原理

想要保证不会出现指令重排就再ready变量加上volatile,他可以保证他前面的指令是顺序执行的

四、volatile原理

volatile的底层实现原理是内存屏障,Memory Barrier

  • 对volatile变量的写指令后会加入写屏障
  • 对volatile变量的读指令后会加入读屏障

 1、可见性保证

写屏障保证在该屏障之前的,对共享变量的改动,都同步到主存中。这样别的线程就都能看到最终同步到主存的数据,从而避免了指令重排的问题。

Java内存模型(JMM)和volatile原理

而读屏障保证在该屏障后,对共享变量的读取,加载的是主存中的最新数据,意思是读取的时候不会读自己本地的,而是去主存中读取

2、有序性保证

写屏障会保证指令重排的时候,不会把写屏障之前的代码排在写屏障之后,他前面的一定是在他前面写入主存的。

读屏障会保证指令重排的时候,不会让读屏障之后的代码排在读屏障的前面先读

注意:有序性的保证只能保证本线程内相关代码不被重排序

五、线程安全的单例

判断是否为null和new这个步骤可能有线程安全问题,所以要加syn,他是进来先判断有没有的,没有才创建,所以也叫懒惰初始化

Java内存模型(JMM)和volatile原理

但是这样做可能有问题,就是我每次都要加syn进来判断是不是为null,这个上锁是有开销的,如果第一次创建成功后,后面的判断都是不成功还要加锁很浪费性能

这个时候我们就可以用双重判断,外面加一个if来判断是不是null,如果不是null都不用加锁了

Java内存模型(JMM)和volatile原理

但是这样可以有问题:指令重排的问题,在字节码文件的时候有空new对象构造器构造和给INSTANCE赋值的顺序变化,如果先赋值但是还构造的时候,别人判断到这个对象已经被赋值了不为null所以直接return这个没构造好的对象了

虽然说synchronized可以保证对象的有序性,但是前提是要让synchronized完全管理这个对象,但是现在这个INSTANCE这个对象是放到外面的,synchronized只是赋值

解决方案:

其实就是在INSTANCE这个变量上加个volatile就行了

因为volatile会加写屏障,他可以保证他之前的指令一定在前面执行,所以这个赋值前必须先构造,所以赋值的时候一定被构造了。

问题:

单例的类为什么要加final?

因为final的类不能别继承,如果子类实现了这个类然后重写方法,重写可能会破坏单例

如果实现了序列化接口,还要做什么防止返序列化来破坏单例?

对象的创建不一定是通过new来创建的,如果我们实现了序列化接口,我们反序列化的时候,也会创建新对象,其实解决就是加上一个readResovle方法,反序列化会调用,直接返回这个对象就可以了不反序列字节码的那个结果

Java内存模型(JMM)和volatile原理文章来源地址https://www.toymoban.com/news/detail-490755.html

到了这里,关于Java内存模型(JMM)和volatile原理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • JMM(Java 内存模型)详解

    为什么要弄一个 CPU 高速缓存(CPU Cauche)呢? 类比我们开发网站后台系统使用的缓存(比如 Redis)是为了解决程序处理速度和访问常规关系型数据库速度不对等的问题。 CPU 缓存则是为了解决 CPU 处理速度和内存处理速度不对等的问题。 我们甚至可以把内存可以看作外存的高

    2024年02月12日
    浏览(45)
  • JMM(Java内存模型)详解

    ​ JMM 是Java内存模型( Java Memory Model),简称JMM。它本身只是一个抽象的概念,并不真实存在,它描述的是一种规则或规范,是和多线程相关的一组规范。通过这组规范,定义了程序中对各个变量(包括实例字段,静态字段和构成数组对象的元素)的访问方式。需要每个JVM

    2023年04月08日
    浏览(42)
  • 了解JAVA内存模型(JMM)

    我们常说的JMM指的是Java内存模型(Java Memory Model,JMM),主要用于控制Java程序解决线程间如何通信和数据同步,JMM规范了多线程访问共享内存时的 可见性、有序性和原子性 。 所有的共享变量都存在 主内存 中; 每个线程 都保存了一份该线程使用到的 共享变量的副本 。 如

    2024年02月06日
    浏览(42)
  • [JVM] 浅谈JMM(Java 内存模型)

    Java 内存模型(Java Memory Model,JMM)是 Java 虚拟机规范中定义的一种抽象计算机内存模型,用于描述 Java 程序在多线程下的内存访问行为。JMM 定义了线程之间共享变量的可见性和有序性规则,为开发者提供了一种可靠的同步机制,以避免并发程序中常见的线程安全问题。 JMM

    2024年01月16日
    浏览(55)
  • Java内存区域(运行时数据区域)和内存模型(JMM)

    Java 内存区域和内存模型是不一样的东西,内存区域是指 Jvm 运行时将数据分区域存储,强调对内存空间的划分。 而内存模型(Java Memory Model,简称 JMM )是定义了线程和主内存之间的抽象关系,即 JMM 定义了 JVM 在计算机内存(RAM)中的工作方式,如果我们要想深入了解Java并发

    2024年02月12日
    浏览(48)
  • 【Java多线程学习7】JMM(Java内存模型)学习

    JMM(Java内存模型),可以看作是 Java定义的并发编程相关的一组规范 ,除了抽象了 线程和主内存 之间的关系之外,其还规定了从 Java源代码 到 CPU可执行指令 的这个转化过程中要遵守哪些并发相关的原则和规范,其主要目的是 简化多线程编程 , 增强程序的可移植性 。 至于

    2024年02月11日
    浏览(41)
  • 区分什么是Java内存模型(JMM)和 JVM运行时数据区

    Java的内存区域和内存模型是不一样的东西,内存区域是指 JVM 运行时将数据分区域存储,强调对内存空间的划分 。 而内存模型(Java Memory Model,简称 JMM )是 定义了线程和主内存之间的抽象关系,即 JMM 定义了 JVM 在计算机内存(RAM)中的工作方式 ,如果我们要想深入了解Java并

    2024年02月11日
    浏览(51)
  • 并发编程之三大特性及JMM内存模型

    目录 原子性 如何保证原子性 可见性 如何保证可见性 有序性 如何保证有序性 Java内存模型(JMM内存模型) Java内存模型的一些关键概念: 主内存与工作内存交互协议 Java内存模型通过以下手段来确保多线程程序的正确性: 锁机制 volatile volatile禁止指令重排序  Happens-Before 并发三

    2024年01月19日
    浏览(45)
  • 剑指JUC原理-8.Java内存模型

    👏作者简介:大家好,我是爱吃芝士的土豆倪,24届校招生Java选手,很高兴认识大家 📕系列专栏:Spring源码、JUC源码 🔥如果感觉博主的文章还不错的话,请👍三连支持👍一下博主哦 🍂博主正在努力完成2023计划中:源码溯源,一探究竟 📝联系方式:nhs19990716,加我进群

    2024年02月06日
    浏览(50)
  • Java中的volatile关键字实现原理

    在并发编程中,线程之间的可见性问题是非常重要的一项难题。Java中提供了一种解决并发可见性问题的机制,即volatile。 在本文中,我们将会讲解Java中volatile的实现原理,为什么它能够保证可见性,以及背后的实现原理涉及到的内存屏障和JVM屏障等内容。在学习

    2023年04月27日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包