详解Java中的内存屏障

 更新时间:2021年05月10日 09:24:24   作者:青年it男  
这篇文章主要介绍了Java中的内存屏障的相关资料,帮助大家更好的理解和学习使用Java,感兴趣的朋友可以了解下

为什么要有内存屏障

这个是为了解决因为cpu,高速缓存,主内存出现的时候,导致的可见性和重序性问题,什么问题呢,看下面
我们都知道计算机运算任务需要CPU和内存相互配合共同完成,其中CPU负责逻辑计算,内存负责数据存储。CPU要与内存进行交互,如读取运算数据、存储运算结果等。由于内存和CPU的计算速度有几个数量级的差距,为了提高CPU的利用率,现代处理器结构都加入了一层读写速度尽可能接近CPU运算速度的高速缓存来作为内存与CPU之间的缓冲:将运算需要使用的数据复制到缓存中,让CPU运算可以快速进行,计算结束后再将计算结果从缓存同步到主内存中,这样处理器就无须等待缓慢的内存读写了。就像下面这样

高速缓存的引入解决了CPU和内存之间速度的矛盾,但是在多CPU系统中也带来了新的问题:可见性问题和重排序问题。

首先是可见性问题:假设有两个线程A、B分别在两个不同的CPU上运行,它们共享同一个变量X。如果线程A对X进行修改后,并没有将X更新后的结果同步到主内存,则变量X的修改对B线程是不可见的。这样就会造成可见性问题

然后是重排序问题:假设A、B两个线程共享两个变量X、Y,A和B分别在不同的CPU上运行。在A中先更改变量X的值放到高速缓存区,然后再更改变量Y的值放到高速缓存区。这时有可能发生Y的值被同步回主内存,而X的值没有同步回主内存的情况,此时对于B线程来说是无法感知到X变量被修改的,或者可以认为对于B线程来说,Y变量的修改被重排序到了X变量修改的前面。

就是为了解决上面的多线程里面的可见性和重序性问题,所以有了下面的内存屏障技术

内存屏障的主要组成

首先是硬件上面的内存屏障

  • Load屏障,是x86上的”ifence“指令,在其他指令前插入ifence指令,可以让高速缓存中的数据失效,强制当前线程从主内存里面加载数据
  • Store屏障,是x86的”sfence“指令,在其他指令后插入sfence指令,能让当前线程写入高速缓存中的最新数据更新写入主内存,让其他线程可见。

Java里面的内存屏障

在java里面有4种,就是 LoadLoad,StoreStore,LoadStore,StoreLoad,实际上也能看出来,这四种都是上面的两种的组合产生的

LoadLoad屏障:

举例语句是Load1; LoadLoad; Load2(这句里面的LoadLoad里面的第一个Load对应Load1加载代码,然后LoadLoad里面的第二个Load对应Load2加载代码),此时的意思就是在Load2加载代码在要读取的数据之前,保证Load1加载代码要从主内存里面读取的数据读取完毕。

StoreStore屏障:

举例语句是 Store1; StoreStore; Store2(这句里面的StoreStore里面的第一个Store对应Store1存储代码,然后StoreStore里面的第二个Store对应Store2存储代码)。此时的意思就是在Store2存储代码进行写入操作执行前,保证Store1的写入操作已经把数据写入到主内存里面,确认Store1的写入操作对其它处理器可见。

LoadStore屏障:

举例语句是 Load1; LoadStore; Store2(这句里面的LoadStore里面的Load对应Load1加载代码,然后LoadStore里面的Store对应Store2存储代码),此时的意思就是在Store2存储代码进行写入操作执行前,保证Load1加载代码要从主内存里面读取的数据读取完毕。

举例语句是 Load1; LoadStore; Store2(这句里面的LoadStore里面的Load对应Load1加载代码,然后LoadStore里面的Store对应Store2存储代码),此时的意思就是在Store2存储代码进行写入操作执行前,保证Load1加载代码要从主内存里面读取的数据读取完毕。

StoreLoad屏障:

举例语句是Store1; StoreLoad; Load2(这句里面的StoreLoad里面的Store对应Store1存储代码,然后StoreLoad里面的Load对应Load2加载代码),在Load2加载代码在从主内存里面读取的数据之前,保证Store1的写入操作已经把数据写入到主内存里面,确认Store1的写入操作对其它处理器可见。

Volatile关键字里面的内存屏障是起作用的

在每个volatile写操作前插入StoreStore屏障,这样就能让其他线程修改A变量后,把修改的值对当前线程可见,在写操作后插入StoreLoad屏障,这样就能让其他线程获取A变量的时候,能够获取到已经被当前线程修改的值

在每个volatile读操作前插入LoadLoad屏障,这样就能让当前线程获取A变量的时候,保证其他线程也都能获取到相同的值,这样所有的线程读取的数据就一样了,在读操作后插入LoadStore屏障;这样就能让当前线程在其他线程修改A变量的值之前,获取到主内存里面A变量的的值。

以上就是详解Java中的内存屏障的详细内容,更多关于Java 内存屏障的资料请关注脚本之家其它相关文章!

相关文章

  • 使用Java编写一个文件批量重命名工具

    使用Java编写一个文件批量重命名工具

    这篇文章主要为大家详细介绍了如何使用Java编写一个文件批量重命名工具,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-12-12
  • SpringBoot详解Banner的使用

    SpringBoot详解Banner的使用

    这篇文章主要介绍了超个性修改SpringBoot项目的启动banner的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • Java反射的应用之动态代理深入理解

    Java反射的应用之动态代理深入理解

    这篇文章主要介绍了Java反射的应用之动态代理深入理解,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-09-09
  • Spring Boot CLI安装教程

    Spring Boot CLI安装教程

    Spring Boot是一个命令行工具,用于使用Spring进行快速原型搭建。本文重点给大家介绍Spring Boot CLI安装教程,感兴趣的朋友参考下吧
    2017-08-08
  • SpringCloud Stream消息驱动实例详解

    SpringCloud Stream消息驱动实例详解

    这篇文章主要介绍了SpringCloud Stream消息驱动的相关知识,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-03-03
  • Java中的CAS和自旋锁详解

    Java中的CAS和自旋锁详解

    这篇文章主要介绍了Java中的CAS和自旋锁详解,CAS算法(Compare And Swap),即比较并替换,是一种实现并发编程时常用到的算法,Java并发包中的很多类都使用了CAS算法,需要的朋友可以参考下
    2023-10-10
  • 使用Java8中Optional机制的正确姿势

    使用Java8中Optional机制的正确姿势

    我们知道 Java 8 增加了一些很有用的 API, 其中一个就是 Optional,下面这篇文章主要给大家介绍了关于如何正确使用Java8中Optional机制的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴,下面来一起看看吧。
    2017-11-11
  • Java创建和填充PDF表单域方法

    Java创建和填充PDF表单域方法

    在本篇文章中小编给大家分享了关于Java创建和填充PDF表单域方法和步骤,有需要的朋友们学习下。
    2019-01-01
  • java实现LFU算法的示例代码

    java实现LFU算法的示例代码

    LFU(Least Frequently Used)算法根据数据的历史访问频率来淘汰数据,其核心思想是“如果数据过去被访问多次,那么将来被访问的频率也更高”,本文为大家整理了Java实现LFU算法的示例代码,需要的可以参考下
    2023-11-11
  • SpringMVC返回图片的几种方式(小结)

    SpringMVC返回图片的几种方式(小结)

    这篇文章主要介绍了SpringMVC返回图片的几种方式(小结),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01

最新评论