Volatile只会用不知道原理?一篇文章带你深究volatile

简介: 要了解并发编程,首先就需要了解并发编程的三大特性:可见性、原子性和有序性。我们今天要讲的volatile保证了可见性和有序性,但是不保证原子性。接下来会通过几段代码和几张图来强化对volatile的了解。

听说微信搜《Java鱼仔》真的可以变强哦!


(一)概述


要了解并发编程,首先就需要了解并发编程的三大特性:可见性、原子性和有序性。我们今天要讲的volatile保证了可见性和有序性,但是不保证原子性。接下来会通过几段代码和几张图来强化对volatile的了解。


(二)volatile保证可见性


在讲JMM的时候,我们写了一段程序,并知道了两个不同线程之间操作数据是不可见的,即线程B修改了主内存中的共享变量后,线程A是不知道的。这就是线程之间的不可见性。


publicclassTest {
privatestaticbooleanflag=false;
publicstaticvoidmain(String[] args) throwsInterruptedException {
newThread(newRunnable() {
@Overridepublicvoidrun() {
System.out.println("waiting");
while (!flag){}
System.out.println("in");
            }
        }).start();
Thread.sleep(2000);
newThread(newRunnable() {
@Overridepublicvoidrun() {
System.out.println("change flag");
flag=true;
System.out.println("change success");
            }
        }).start();
    }
}

这段代码的结果是第二个线程修改flag的值不会被第一个线程见到


网络异常,图片无法展示
|


现在我们做个小小的改变,给flag加上volatile修饰词


privatestaticvolatilebooleanflag=false;

网络异常,图片无法展示
|


对volatile原理的理解还是需要借助JMM,我们拿上来第一段代码的执行流程图:


网络异常,图片无法展示
|


这是未加volatile时的执行过程,最后会停留在第十步,flag会变成true,但是线程A不知道。


加上volatile后,当主内存的flag被改变时,volatile通过cpu的总线嗅探机制,将其他也正在使用该变量的线程的数据失效掉,使得这些线程要重新读取主内存中的值,最后线程A就发现flag的值被改变了。


(三)Volatile保证有序性


Volatile通过内存屏障禁止指令的重排序,从而保证执行的有序性。具体的内容我在指令重排序和内存屏障的时候讲到了,有兴趣的小伙伴可以看一下。


(四)Volatile不保证原子性


首先还是拿出一段代码,这段代码很简单,定义一个count,并且用volatile修饰。接着创建十个线程,每个线程循环1000次count++ :

publicclassVolatileAtomSample {
privatestaticvolatileintcount=0;
publicstaticvoidmain(String[] args) {
for (inti=0; i<10; i++) {
newThread(newRunnable() {
@Overridepublicvoidrun() {
for (intj=0; j<1000; j++) {
count++;
                    }
                }
            }).start();
        }
try {
Thread.sleep(1000);
        } catch (InterruptedExceptione) {
e.printStackTrace();
        }
System.out.println(count);
    }
}

最后无论执行多少次,你会发现最后输出的count绝大多数都是小于10000,现象已经体现出了volatile不能保证原子性,但是为什么呢?


网络异常,图片无法展示
|


还是通过一个流程图来表示,当线程A执行完count+1后,将值写回到主内存,这个时候由于volatile的可见性,其他工作内存中的count值会被失效掉重新赋值。


可如果线程B刚好执行到第四步呢,线程B工作内存中的count因为volatile变成了1,assign赋值后的count还是等于1,在这里直接少了一次count++。这就是volatile不能保证原子性的原因。


(五)Volatile的使用场景


通过一个很经典的场景来展示一下volatile的应用,双重校验单例:


publicclassSingleton {
privatestaticSingletonInstance;
privateSingleton(){};
publicstaticSingletongetInstance(){
if (Instance==null){
synchronized (Singleton.class){
Instance=newSingleton();
            }
        }
returnInstance;
    }
}

上面这段代码相信大家肯定很熟悉,单例模式最经典的一段代码,获取实例对象时如果为空就初始化,如果不为空就返回实例,看着没有问题,但是在高并发环境下这段代码是会出问题的。 Instance=new Singleton(); 实例化一个对象时,需要经历三步:


网络异常,图片无法展示
|


注意,这三步是有可能发生指令重排序的,因此有可能是先申请内存空间,再把对象赋值到内存里,最后实例化对象。第一步->第三步->第二步的方式来执行。


当此时有两个线程A和B同时申请对象的时候,当线程A执行到重排序后的第二步时


网络异常,图片无法展示
|


线程B执行了if (Instance==null)这行代码,因为此时instance已经赋值到内存里了,所以会直接return Instance; 但是!这个对象并没有被实例化,因此线程B调用该实例时,就报错了。


这个问题的解决办法就是volatile禁止重排序


privatestaticvolatileSingletonInstance;

(六)Volatile可能会导致的问题


凡事都讲究一个度,volatile也是。如果一个程序中用了大量的volatile,就有可能会导致总线风暴,所谓总线风暴,就是指当volatile修饰的变量发生改变时,总线嗅探机制机会将其他内存中的这个变量失效掉,如果volatile很多,无效交互会导致总线带宽达到峰值。因此对volatile的使用也需要适度。



相关文章
|
应用服务中间件 程序员 数据库
带你读《2022技术人的百宝黑皮书》——开发规约的意义与细则(3)
带你读《2022技术人的百宝黑皮书》——开发规约的意义与细则(3)
196 0
|
10月前
|
前端开发 API UED
React 按需加载 Lazy Loading
随着 Web 应用复杂度增加,页面加载速度成为影响用户体验的关键因素。React 提供了按需加载(Lazy Loading)功能,通过 `React.lazy` 和 `Suspense` 实现动态加载组件,减少初始加载时间,提升性能。本文从基础概念入手,探讨常见问题、易错点及解决方案,并通过代码示例详细说明。
471 1
|
7月前
|
存储 安全 Linux
NFS使用TrueNAS SCALE的好处
从Linux自带的NFS转向TrueNAS SCALE,因其不仅提供块服务(iSCSI),还内置压缩功能。通过WEB界面开启SSH并设置Allow Password Authentication。安全配置方面,限制特定IP访问NFS输出。对比发现,对于文本文件,TrueNAS SCALE展现出显著的压缩优势,如日志文件压缩率接近90%,大大节省了存储空间,而对已压缩文件则无明显变化。这一转变有效优化了存储效率和安全性。
232 0
|
6月前
|
SQL 监控 数据库
如何解决 SQL Server 占用内存过多问题
SQL Server 占用过多内存会导致响应缓慢和查询性能低下。解决流程包括:1) 查看内存使用情况,2) 分析各数据库内存占用,3) 优化 SQL Server 配置(如限制最大内存),4) 优化查询(如创建索引),5) 持续监控效果。通过这些步骤可有效控制内存占用,提升系统性能。
785 0
|
存储 弹性计算 大数据
阿里云服务器怎么样?谈谈阿里云服务器的使用感受
阿里云服务器ECS是一种安全可靠的云计算服务,具备弹性伸缩能力,支持多种实例规格如ECS经济型e、通用算力型u1、计算型c7等。用户可根据需求选择不同计算架构、存储类型(云盘、本地盘)及网络方案(VPC专有网络)。ECS提供快照备份、多种镜像类型及灵活的计费模式(包年包月、按量付费等)。阿里云还提供免费试用计划,适用于个人和企业用户。ECS优势包括稳定、弹性、安全性高及高性能等特点。广泛应用于Web应用、在线游戏、大数据分析及深度学习等多个场景。知名用户包括新浪微博、火花思维等。
|
11月前
|
Java Linux Windows
windows版java版本管理器
本文介绍了如何在Windows上使用JDK版本管理器jvms来管理多个Java版本,包括下载、初始化、列出本地安装的JDK版本、在线查看可用版本、切换和安装特定版本的Java。
1989 0
windows版java版本管理器
|
缓存 Linux
更新yum源的保姆级教程(有手就行)
更新yum源的保姆级教程(有手就行)
|
Java 知识图谱
知识图谱(Knowledge Graph)- Neo4j 5.10.0 使用 - Java SpringBoot 操作 Neo4j
知识图谱(Knowledge Graph)- Neo4j 5.10.0 使用 - Java SpringBoot 操作 Neo4j
638 0
|
负载均衡 Java 开发者
Spring Cloud:一文读懂其原理与架构
Spring Cloud 是一套微服务解决方案,它整合了Netflix公司的多个开源框架,简化了分布式系统开发。Spring Cloud 提供了服务注册与发现、配置中心、消息总线、负载均衡、熔断机制等工具,让开发者可以快速地构建一些常见的微服务架构。
|
负载均衡 监控 前端开发
Feign 与 OpenFeign
Feign 与 OpenFeign
333 0