找回密码
 立即注册
首页 业界区 业界 Java源码分析系列笔记-3.volatile

Java源码分析系列笔记-3.volatile

臧莞然 2025-6-22 17:46:15
目录

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

    • 2.1. 例子
    • 2.2. 无法停止的原因分析
    • 2.3. 解决方法
    • 2.4. volatile vs synchronized

  • 3. 汇编源码实验

    • 3.1. 下载编译 hsdis-amd64.dll
    • 3.2. 放入 JRE bin 目录下
    • 3.3. 对比实验
    • 3.4. 加上 jvm 参数运行
    • 3.5. 输出结果对比

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

    • 4.1. 可见性
    • 4.2. 有序性

  • 5. 参考

1. 是什么

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

  • 轻量级
    相比于 synchronized,volatile 不会引起上下文切换(不会造成线程阻塞)
  • 原子性
    对任意单个 volatile 变量的读/写具有原子性,但类似于 volatile++这种复合操作不具有原子性
  • 可见性
    volatile 写会把数据同时写入主内存,并让其他线程对这个数据的工作内存失效,这样其他线程读的时候就需要去主内存中读取
  • 有序性
    对一个 volatile 变量的读,总是能看到(任意线程)对这个 volatile 变量最后的写入。
2. 什么情况 volatile 比 synchronized 更合适

2.1. 例子

如下程序。thread1 并不会停止
  1. public class VolatileTest
  2. {
  3.     private static boolean isRunning = true;
  4.     public static void main(String[] args) throws InterruptedException
  5.     {
  6.         Thread thread1 = new Thread(()->{
  7.             System.out.println("thread1 is running");
  8.             while (isRunning)
  9.             {
  10.             }
  11.             System.out.println("thread1 will be stopped");
  12.         });
  13.         thread1.start();
  14.         Thread.sleep(1000);
  15.         Thread thread2 = new Thread(()->{
  16.             System.out.println("thread2 is running");
  17.             isRunning = false;
  18.             System.out.println("thread2 change isRunning flag");
  19.         });
  20.         thread2.start();
  21.         thread1.join();
  22.         thread2.join();
  23.     }
  24. }
复制代码
2.2. 无法停止的原因分析

1.png


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

将 isRunning 使用 volatile 修饰
  1. public class VolatileTest
  2. {
  3.     private static volatile boolean isRunning = true;
  4.     public static void main(String[] args) throws InterruptedException
  5.     {
  6.         Thread thread1 = new Thread(()->{
  7.             System.out.println("thread1 is running");
  8.             while (isRunning)
  9.             {
  10.             }
  11.             System.out.println("thread1 will be stopped");
  12.         });
  13.         thread1.start();
  14.         Thread.sleep(1000);
  15.         Thread thread2 = new Thread(()->{
  16.             System.out.println("thread2 is running");
  17.             isRunning = false;
  18.             System.out.println("thread2 change isRunning flag");
  19.         });
  20.         thread2.start();
  21.         thread1.join();
  22.         thread2.join();
  23.     }
  24. }
复制代码
2.4. volatile vs synchronized

volatilesynchronized内存模型三性可见性、有序性可见性、有序性、原子性是否造成线程阻塞【重量级别】不会会应用范围变量级别变量、方法、类级别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 目录下

2.png

3.3. 对比实验


  • 有 volatile
  1. public class TestVolatile
  2. {
  3.     private static volatile int i = 0;
  4.     public static void main(String[] args)
  5.     {
  6.         test();
  7.     }
  8.     private static void test()
  9.     {
  10.         i++;
  11.     }
  12. }
复制代码

  • 没有 volatile
  1. public class TestVolatile
  2. {
  3.     private static int i = 0;
  4.     public static void main(String[] args)
  5.     {
  6.         test();
  7.     }
  8.     private static void test()
  9.     {
  10.         i++;
  11.     }
  12. }
复制代码
3.4. 加上 jvm 参数运行
  1. -server -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:-Inline -XX:CompileCommand=print,*TestVolatile.test
复制代码
使用 IDEA 的话如下图:
3.png

3.5. 输出结果对比

结果如附件:

  • volatile.txt
  • 普通.txt
    使用 BeyondCompare 对比图如下:
    4.png

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

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

  • 禁止屏障两边的指令重排序
  • 强制把写缓冲区/高速缓存中的脏数据等写回主内存,让缓存中相应的数据失效
4.1. 可见性

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

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

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

在 volatile 写之前插入释放屏障【LoadStore+StoreStore】使得该屏障之前的任何读写操作都先于这个 volatile 写被提交;
在 volatile 读之后插入获取屏障【LoadLoad+LoadStore】使得这个 volatile 读操作先于该屏障之后的任何读写操作被提交。
5. 参考


  • 深入理解 Java 内存模型(四)——volatile-InfoQ
  • 再有人问你 volatile 是什么,把这篇文章也发给他。-HollisChuang's Blog
  • Java volatile 关键字底层实现原理解析 - 王泽远的博客 | Crow's Blog
  • 精确解释 java 的 volatile 之可见性、原子性、有序性(通过汇编语言) - tantexian 的博客空间 - OSCHINA
  • volatile 与 synchronized 的区别 - 掘金

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册