1. 是什么

Java 的轻量级锁,主要保证了有序性、可见性和一定的原子性

  • 轻量级

    相比于 synchronized,volatile 不会引起上下文切换(不会造成线程阻塞)
  • 原子性

    对任意单个 volatile 变量的读/写具有原子性,但类似于 volatile++这种复合操作不具有原子性
  • 可见性

    volatile 写会把数据同时写入主内存,并让其他线程对这个数据的工作内存失效,这样其他线程读的时候就需要去主内存中读取
  • 有序性

    对一个 volatile 变量的读,总是能看到(任意线程)对这个 volatile 变量最后的写入。

2. 什么情况 volatile 比 synchronized 更合适

2.1. 例子

如下程序。thread1 并不会停止

public class VolatileTest
{
private static boolean isRunning = true; public static void main(String[] args) throws InterruptedException
{
Thread thread1 = new Thread(()->{
System.out.println("thread1 is running");
while (isRunning)
{ }
System.out.println("thread1 will be stopped");
});
thread1.start(); Thread.sleep(1000); Thread thread2 = new Thread(()->{
System.out.println("thread2 is running"); isRunning = false; System.out.println("thread2 change isRunning flag");
});
thread2.start(); thread1.join();
thread2.join();
}
}

2.2. 无法停止的原因分析

  1. Thread1 从主内存把 isRunning 这个变量加载到工作内存中,值为 true 所以一直运行
  2. Thread2 从主内存把 isRunning 这个变量加载到工作内存中,值为 true 改为 false,写回工作内存,再写回主内存
  3. Thread1 一直从工作内存中读取这个变量,一直为 true,所以还是无法停止运行

2.3. 解决方法

将 isRunning 使用 volatile 修饰

public class VolatileTest
{
private static volatile boolean isRunning = true; public static void main(String[] args) throws InterruptedException
{
Thread thread1 = new Thread(()->{
System.out.println("thread1 is running");
while (isRunning)
{ }
System.out.println("thread1 will be stopped");
});
thread1.start(); Thread.sleep(1000); Thread thread2 = new Thread(()->{
System.out.println("thread2 is running"); isRunning = false; System.out.println("thread2 change isRunning flag");
});
thread2.start(); thread1.join();
thread2.join();
}
}

2.4. volatile vs synchronized

volatile synchronized
内存模型三性 可见性、有序性 可见性、有序性、原子性
是否造成线程阻塞【重量级别】 不会
应用范围 变量级别 变量、方法、类级别

3. 汇编源码实验

3.1. 下载编译 hsdis-amd64.dll

参考How to build hsdis-amd64.dll and hsdis-i386.dll on Windows或者hsdis-amd64.7z

3.2. 放入 JRE bin 目录下

3.3. 对比实验

  • 有 volatile
public class TestVolatile
{
private static volatile int i = 0;
public static void main(String[] args)
{
test();
} private static void test()
{
i++;
}
}
  • 没有 volatile
public class TestVolatile
{
private static int i = 0;
public static void main(String[] args)
{
test();
} private static void test()
{
i++;
}
}

3.4. 加上 jvm 参数运行

-server -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:-Inline -XX:CompileCommand=print,*TestVolatile.test

使用 IDEA 的话如下图:

3.5. 输出结果对比

结果如附件:

4. 根据实验结果分析原理

从汇编语言层面看,有 volatile 的结果比没有 volatile 的多了一个指令:lock addl $0x0,(%rsp) ,这条指令起到内存屏障的作用

  1. 禁止屏障两边的指令重排序

  2. 强制把写缓冲区/高速缓存中的脏数据等写回主内存,让缓存中相应的数据失效

4.1. 可见性

根据内存屏障的作用 2 可以实现可见性,表现如下

  • volatile 写会把数据同时写入主内存,并让其他线程对这个数据的工作内存失效
  • 其他线程 volatile 读的时候就需要去主内存中读取

4.2. 有序性

根据内存屏障的作用 1 可以实现有序性,表现如下

在 volatile 写之前插入释放屏障【LoadStore+StoreStore】使得该屏障之前的任何读写操作都先于这个 volatile 写被提交;

在 volatile 读之后插入获取屏障【LoadLoad+LoadStore】使得这个 volatile 读操作先于该屏障之后的任何读写操作被提交。

5. 参考

Java源码分析系列笔记-3.volatile的更多相关文章

  1. Java源码分析系列之HttpServletRequest源码分析

    从源码当中 我们可以 得知,HttpServletRequest其实 实际上 并 不是一个类,它只是一个标准,一个 接口而已,它的 父类是ServletRequest. 认证方式 public int ...

  2. Java源码分析系列

    1) 深入Java集合学习系列:HashMap的实现原理 2) 深入Java集合学习系列:LinkedHashMap的实现原理 3) 深入Java集合学习系列:HashSet的实现原理 4) 深入Ja ...

  3. MyCat源码分析系列之——结果合并

    更多MyCat源码分析,请戳MyCat源码分析系列 结果合并 在SQL下发流程和前后端验证流程中介绍过,通过用户验证的后端连接绑定的NIOHandler是MySQLConnectionHandler实 ...

  4. spring源码分析系列 (2) spring拓展接口BeanPostProcessor

    Spring更多分析--spring源码分析系列 主要分析内容: 一.BeanPostProcessor简述与demo示例 二.BeanPostProcessor源码分析:注册时机和触发点 (源码基于 ...

  5. MyCat源码分析系列之——BufferPool与缓存机制

    更多MyCat源码分析,请戳MyCat源码分析系列 BufferPool MyCat的缓冲区采用的是java.nio.ByteBuffer,由BufferPool类统一管理,相关的设置在SystemC ...

  6. MyCat源码分析系列之——配置信息和启动流程

    更多MyCat源码分析,请戳MyCat源码分析系列 MyCat配置信息 除了一些默认的配置参数,大多数的MyCat配置信息是通过读取若干.xml/.properties文件获取的,主要包括: 1)se ...

  7. [Tomcat 源码分析系列] (二) : Tomcat 启动脚本-catalina.bat

    概述 Tomcat 的三个最重要的启动脚本: startup.bat catalina.bat setclasspath.bat 上一篇咱们分析了 startup.bat 脚本 这一篇咱们来分析 ca ...

  8. MyBatis 源码分析系列文章导读

    1.本文速览 本篇文章是我为接下来的 MyBatis 源码分析系列文章写的一个导读文章.本篇文章从 MyBatis 是什么(what),为什么要使用(why),以及如何使用(how)等三个角度进行了说 ...

  9. spring源码分析系列 (8) FactoryBean工厂类机制

    更多文章点击--spring源码分析系列 1.FactoryBean设计目的以及使用 2.FactoryBean工厂类机制运行机制分析 1.FactoryBean设计目的以及使用 FactoryBea ...

  10. spring源码分析系列 (5) spring BeanFactoryPostProcessor拓展类PropertyPlaceholderConfigurer、PropertySourcesPlaceholderConfigurer解析

    更多文章点击--spring源码分析系列 主要分析内容: 1.拓展类简述: 拓展类使用demo和自定义替换符号 2.继承图UML解析和源码分析 (源码基于spring 5.1.3.RELEASE分析) ...

随机推荐

  1. PVE常用命令

    1.查看集群下的节点信息 root@pve63-node172:~# pvecm nodes Membership information ---------------------- Nodeid ...

  2. Delphi edit只允许输入数字

    if not (key in ['0'..'9',#8]) then key := #0;

  3. 【网络攻防】ARP欺骗实验

    实验概述 ARP欺骗是一类地址欺骗类病毒,属于木马病毒,自身不具备主动传播的特性,不会自我复制.但是由于其发作的时候会不断向全网发送伪造的ARP数据包,导致网络无法正常运行,严重的甚至可能带来整个网络 ...

  4. 【C#】Winform监听USB串口设备拔插实现自动断开

    [C#]Winform监听USB串口设备拔插 零.问题 最近在开发串口相关的软件,需要检测串口拔掉后主动关闭串口,因此需要检测到USB的拔插事件. 一.解决 在主窗口From类下覆盖WndProc方法 ...

  5. FireDAC开发DataSnap应用系统【2】-使用TFDJSONDatasets功能

    类别 说明 TFDJSONDatasets FireDAC使用JSONDataSet,其中可包括多个DataSet对象 TFDJSONDataSetsWriter 把TDataSet写入TFDJSON ...

  6. FastAPI与SQLAlchemy数据库集成与CRUD操作

    title: FastAPI与SQLAlchemy数据库集成与CRUD操作 date: 2025/04/16 09:50:57 updated: 2025/04/16 09:50:57 author: ...

  7. Linux部署调度工具xxl-job

    背景: Pentaho Data Integration(kettle)作为用户规模最多的开源ETL工具,强大简洁的功能深受广大ETL从业者的欢迎.但kettle本身的调度监控功能却非常弱.Penta ...

  8. Windows系统设置开机自启动+分块压缩+文件共享

    开机自启动+分块压缩+文件共享 一.设置开机自启动 win+R 打开运行窗口,输入 shell:startup 此时桌面会弹出一个目录文件夹,只需要将需要启动的软件放入该文件夹即可开机自启. C:\U ...

  9. Apache Flink(CVE-2020-17519)路径遍历漏洞复现_附POC和批量检测脚本

    声明 本文仅用于技术交流,请勿用于非法用途 由于传播.利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任. 文章作者拥有对此文章的修改和解释权.如 ...

  10. 牛客小白月赛111 E 构造矩形

    E 构造矩形 原题链接:https://ac.nowcoder.com/acm/contest/102742/E 思路: 这种询问方案数或者"价值"的题,通常解法要么是维护前缀信息 ...