热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Java精通并发volatile与内存屏障的重要语义详细分析

在上一次https:www.cnblogs.comwebor2006protectedp12595201.html咱们已经对于volatile关键字的作用进行了一定的了解,这里回顾

在上一次https://www.cnblogs.com/webor2006/protected/p/12595201.html咱们已经对于volatile关键字的作用进行了一定的了解,这里回顾一下:

Java精通并发-volatile与内存屏障的重要语义详细分析 

上一次对于第一条作用进行了详细的解读了,接下来则来解读一下剩下的两条:防止指令重排序、实现变量的可见性。而这俩其实都是通过一种手段来实现的:内存屏障(memory barrier),所以要想搞清楚这这两条,必须得先来理解内存屏障这个概念,所以接下来重点来搞清楚内存屏障这个平常听得比较少的这个概念。

这其实涉及到JIT(Just In Time)的一些功能,在现代化的JVM编译器当中,它会根据我们所写的代码的情况自动的一定程序的优化,其中优化当中就有一个可能就是会对咱们的指令进行一定的修改,比如按照顺序执行了三条指令:1、2、3【对应我们的代码顺序】,但是在编译完之后可能生成的字节码会变成3、2、1,或1、3、2等,也就是对指令进行重排序了,这里用一个简单的例子来直观的看一下指令重排序的大概思想:

int a = 0;
int b = 1;

a++;

重排后可能为:

int a = 0;
a++;
int b = 0;

对于这个重排序其实是编译器为了让我们的程序执行的性能更高而采取的一种优化手段,但是!!!在极端情况下这种指令重排序的优化手段并不是我们需要的,所以此时就需要防止某些指令重排序,而是按我们所编写的代码的顺序来执行。对于指令重排序而言,在单线程环境下肯定是没任何问题的,如果有问题也不可能出现这种优化策略了,重点是在多线程的环境下这种所谓优化的指令重排序策略可能就会产生问题,而这个volatile关键字就具备这种防止指令重排序的功能。

阐述内存屏障(memeory barrier):

volatile写入操作:

这里先来看一个简单代码:

int a = 1;
String s = "Hello";

volatile boolean v = false; //写入操作

此时则会在volatile这句代码之前和之后插入相应的内存屏障:

int a = 1;
String s = "Hello";
内存屏障
volatile boolean v = false; //写入操作
内存屏障

而内存屏障是存在有分类的,这里给内存屏障再细化一下则为:

int a = 1;
String s = "Hello";
内存屏障 (Release Barrier,释放屏障volatile boolean v = false; //写入操作
内存屏障(Store Barrier,存储屏障

很显然这个屏障我们肉眼是看不到的,是借助于volatile来实现的,那对于这俩个屏障对于程序有啥作用呢?下面来解释一下:

  • Release Barrier:防止下面的volatile与上面的所有操作的指令重排序,并可以让在内存屏障之前所发生的读写操作都能立刻的发布到所有的程序当中,其它线程就能立刻看到其修改的结果。啥意思?
    Java精通并发-volatile与内存屏障的重要语义详细分析
  • Store Barrier:它的重要作用是刷新处理器的缓存,结果是可以确保该存储屏障之前一切的操作所生成的结果对于其他处理器来说都可见,也就是:
    Java精通并发-volatile与内存屏障的重要语义详细分析

volatile读取操作:

对于volatile的读操作其内存屏障又是不一样的,下面来看一下:

int a = 1;
String s = "Hello";
内存屏障 (Release Barrier,释放屏障)
volatile boolean v = false; //写入操作
内存屏障(Store Barrier,存储屏障)

boolean v1 = v; //读取操作

int a = 1;
String s = "Hello";

此时由于遇到了volatile的读取操作,则又会产生内存屏障了,它有别于之前看到的写入的屏障,如下:

int a = 1;
String s = "Hello";
内存屏障 (Release Barrier,释放屏障)
volatile boolean v = false; //写入操作
内存屏障(Store Barrier,存储屏障)

内存屏障 (Load Barrier,加载屏障)
boolean v1 = v; //读取操作
内存屏障 (Acquire Barrier,获取屏障)

int a = 1;
String s = "Hello";

那这两种屏障又有何义呢?

  • Load Barrier:可以刷新处理器缓存,同步其他处理器对该volatile变量的修改结果。也就是:
    Java精通并发-volatile与内存屏障的重要语义详细分析
  • Acquire Barrier:可以防止上面的volatile读取操作与下面的所有操作语句的指令重排序。
    Java精通并发-volatile与内存屏障的重要语义详细分析

可以发现:

Java精通并发-volatile与内存屏障的重要语义详细分析

Java精通并发-volatile与内存屏障的重要语义详细分析

对于volatile关键字变量的读写操作,本质上都是通过内存屏障来执行的,而内存屏障兼具了如下两方面的能力:

1、防止指令重排序。

2、实现变量内存的可见性。

所以:

Java精通并发-volatile与内存屏障的重要语义详细分析

最后总结一下:

1、对于读取操作来说,volatile可以确保该操作与其后续的所有读写操作都不会进行指令重排序。

2、对于修改操作来说,valatile可以确保该操作与其上面的所有读写操作都不会进行指令重排序。

注意:

在上面的举例中都是Java的原生数据类型:

Java精通并发-volatile与内存屏障的重要语义详细分析

如果是一个引用类型呢?比如说ArrayList,那对于volatile的内存屏障功效是不起作用的,为啥?因为ArrayList中的读写操作都不是原子的,比如读操作,得先找到元素的地址,然后再进行读取,但是!!如果将ArrayList的引用赋值给另一个volatile的ArrayList,这就可以确保原子操作,也就有了volatile相关的功效了。

再论volatile和锁【面试题】:

在上一次volatile的学习中已经针对它们俩的相同与不同点做了一个阐述,回忆一下:

Java精通并发-volatile与内存屏障的重要语义详细分析

这里由于学到了内存屏障的知识点,所以需要再拉出来进一步阐述一下,对于synchronized代码块而言,对应的字节码指令我们都知道会是如下:

monitorenter
.....
monitorexit

我们知道锁的功能比volatile功能更强大,因为它有排他性,对于volatile它不是有指令重排序和内存可见性的功效,那锁有木有呢?当然有,所以加上这个功效之后的锁背后的形态就会变为:

monitorenter
内存屏障 (Acquire Barrier,获取屏障)//刷新处理器缓存,同步其他处理器对该volatile变量的修改结果,也就是获取最新的值
.....
内存屏障 (Release Barrier,释放屏障)//也就是将处理结果发布出去,刷新处理器缓存
monitorexit

以上就是对于同步锁的一个完整的形态。

总结:

1、volatile关键字自身的劣势:它相比不使用volatile的变量而言,性能有损失,因为对于有volatile的变量,则每次都是会从主内存(高速缓存)中来获取了,而如果不使用volatile的变量,则会直接从寄存器上获取,要明白,寄存器要比内存获取快多的,所以这个关键字不要烂用。

2、volatile相比锁,优点是volatile不存在阻塞,也不会进行用户态到内核态的切换,而锁肯定是要阻塞且会进行用户和内核态的切换;缺点是它不具备锁的排它性。

通过这两篇的总结,我觉得就已经能彻底来理清这个关键字的含义了,真的涉及到的概念还是很难理解的。


推荐阅读
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 2023年京东Android面试真题解析与经验分享
    本文由一位拥有6年Android开发经验的工程师撰写,详细解析了京东面试中常见的技术问题。涵盖引用传递、Handler机制、ListView优化、多线程控制及ANR处理等核心知识点。 ... [详细]
  • PHP 5.5.0rc1 发布:深入解析 Zend OPcache
    2013年5月9日,PHP官方发布了PHP 5.5.0rc1和PHP 5.4.15正式版,这两个版本均支持64位环境。本文将详细介绍Zend OPcache的功能及其在Windows环境下的配置与测试。 ... [详细]
  • 本文介绍如何使用 Python 将一个字符串按照指定的行和元素分隔符进行两次拆分,最终将字符串转换为矩阵形式。通过两种不同的方法实现这一功能:一种是使用循环与 split() 方法,另一种是利用列表推导式。 ... [详细]
  • Explore how Matterverse is redefining the metaverse experience, creating immersive and meaningful virtual environments that foster genuine connections and economic opportunities. ... [详细]
  • 技术分享:从动态网站提取站点密钥的解决方案
    本文探讨了如何从动态网站中提取站点密钥,特别是针对验证码(reCAPTCHA)的处理方法。通过结合Selenium和requests库,提供了详细的代码示例和优化建议。 ... [详细]
  • 本文介绍了Java并发库中的阻塞队列(BlockingQueue)及其典型应用场景。通过具体实例,展示了如何利用LinkedBlockingQueue实现线程间高效、安全的数据传递,并结合线程池和原子类优化性能。 ... [详细]
  • 深入理解Java中的volatile、内存屏障与CPU指令
    本文详细探讨了Java中volatile关键字的作用机制,以及其与内存屏障和CPU指令之间的关系。通过具体示例和专业解析,帮助读者更好地理解多线程编程中的同步问题。 ... [详细]
  • 将Web服务部署到Tomcat
    本文介绍了如何在JDeveloper 12c中创建一个Java项目,并将其打包为Web服务,然后部署到Tomcat服务器。内容涵盖从项目创建、编写Web服务代码、配置相关XML文件到最终的本地部署和验证。 ... [详细]
  • 本文详细记录了在基于Debian的Deepin 20操作系统上安装MySQL 5.7的具体步骤,包括软件包的选择、依赖项的处理及远程访问权限的配置。 ... [详细]
  • Java 中 Writer flush()方法,示例 ... [详细]
  • 本文介绍如何在QT框架中使用QWebSocket和QTcpSocket实现SSL加密通信,涵盖单向认证设置。单向认证常见于Web通信场景,其中客户端验证服务端证书,而服务端不验证客户端证书。 ... [详细]
  • ASP.NET MVC中Area机制的实现与优化
    本文探讨了在ASP.NET MVC框架中,如何通过Area机制有效地组织和管理大规模应用程序的不同功能模块。通过合理的文件夹结构和命名规则,开发人员可以更高效地管理和扩展项目。 ... [详细]
author-avatar
卡瑙娇管
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有