💪
AndroidCollect
  • 写在前面
  • 计算机基础
    • 计算机组成原理
    • 算法
      • 查找
        • 二分查找
      • 排序
        • 简单排序
        • 高级排序
        • 特殊排序
      • 海量数据
      • 思想
        • 贪心
        • 分治
        • 动态规划
        • 回溯
      • 哈希算法
    • 数据结构
      • 队列
        • 知识点
        • 相关题目
          • 用两个栈实现队列
          • 实现循环队列
          • 用链表实现队列
          • 用数组实现队列
      • 栈
        • 相关算法题目
          • 用链表实现栈
          • 用数组实现栈
      • 链表
        • 知识点梳理
        • 相关算法题目
          • 删除倒数第n个结点
          • 合并两个有序链表
          • 检测单链表是否有环
          • 获取中间结点
          • 反转链表
      • 跳表
      • 哈希表
      • 树
        • 二叉树
        • 二叉查找树
        • AVL 树
        • Trie 树
        • 红黑树
      • 堆
        • 存储
        • 堆的应用
      • 图
    • 网络
      • 应用层协议
        • DNS
        • HTTP
        • HTTPS
      • 传输层协议
        • TCP
        • UDP
      • 输入网址后发生了什么
    • 操作系统
      • 内存
    • 数据库
  • 软件工程
    • 编程思想
    • 设计模式
      • 状态模式
      • 装饰器模式
      • 代理模式
      • 责任链模式
      • 建造者模式
      • 单例模式
      • 观察者模式
  • Java
    • 基础
    • 异常
    • 并发编程
      • ThreadLocal
      • 线程池
      • 理解 volatile
      • AbstractQueuedSynchronizer
    • 集合
      • LinkedHashMap 源码
      • HashMap 源码
    • 注解
    • 反射
      • JDK 动态代理
    • JVM
      • 自动内存管理机制
      • Class 文件格式
      • 类加载机制
      • Java 内存模型(JMM)
      • 字节码指令
      • HotSpot 虚拟机实现细节
    • 源码与原理
    • 各版本主要特性
  • Android
    • 基础组件
      • Context
      • Activity
        • 生命周期
        • 启动模式与任务栈
        • 启动流程
      • Service
      • ContentProvider
      • BroadcastReceiver
      • Fragment
      • View
        • 常用控件问题总结
          • RecyclerView
          • ViewPager2
        • CoordinatorLayout
        • SurfaceView
        • 事件分发
        • 绘制流程
        • 自定义 View
        • Window
    • 数据存储
      • 存储结构
      • Sqlite
      • 序列化
      • SharedPreferences
    • 资源
      • 图片加载
    • 动画
      • 属性动画
    • 线程和进程
      • Binder 机制
      • 跨进程通信
        • AIDL
    • 内部原理
      • 消息循环机制
      • Binder
      • Window
      • SparseArray
      • ArrayMap
      • RecyclerView
      • App 启动流程
    • 性能优化
      • 内存
        • 内存使用优化
        • 内存泄漏
      • 启动优化
      • 缩减包大小
      • 布局优化
      • ANR
    • 打包构建
      • dex 文件
      • APK 打包流程
      • APK 签名流程
    • 架构
      • 运行时
      • Android 系统架构
      • 应用项目架构
    • 开源框架源码或原理
      • RxJava
        • 使用笔记
        • 源码解析
      • Retrofit
      • ButterKnife
      • BlockCanary
      • LeakCanary
      • OkHttp
      • 图片加载
        • Glide
        • Picasso
    • 碎片化处理
      • 屏幕适配
    • 黑科技
      • 热修复
    • Jetpack
      • Lifecycle
      • Room
      • WorkManager
    • 新动态
      • AndroidX
      • 各系统版本特性
  • 开发工具
    • 正则表达式
    • ADB
    • Git
  • Kotlin
  • Flutter
  • 关于作者
  • 致谢
由 GitBook 提供支持
在本页
  • JMM 对 volatile 的特殊规则
  • volatile 的语义
  • 可见性
  • 禁止指令重排序
  • 如何实现可见性和禁止重排序?

这有帮助吗?

  1. Java
  2. 并发编程

理解 volatile

JMM 对 volatile 的特殊规则

JVM 对 volatile 专门定义了特殊的访问规则:

  • 在工作内存中,每次使用变量之前都要先从主内存刷新最新值,以保证可以看到其他线程对该变量修改的值

  • 在工作内存中,每次修改变量后,都需要立刻同步回主内存,保证修改对其他线程可见

  • volatile 修饰的变量不会被指令重排序优化,保证代码的执行顺序与程序的顺序相同

volatile 的语义

可见性

保证变量对所有线程的可见性,可见性是指当一个线程改变了变量的值后,变量的新值在其他线程是可以立即得知的。(普通变量由于需要借助主内存在线程的工作内存中对变量进行周转,无法保证可见性)。虽然 volatile 保证了变量不会出现不一致的情况,但由于对变量执行的操作并不是都是原子性的,所以光靠 volatile 并不能保证线程安全,(例如 i++,需要先读取变量,再自增,再写回),还是需要加锁。

volatile 使用场景

volatile boolean shutdown;

public void shutDown(){
    shutdown=true;
}

public void doWork(){
    while(!shutdown){
        //do stuff
    }
}

禁止指令重排序

使用volatile 的第二个语义是禁止指令重排序优化。普通的变量只能保证在方法的执行过程中,所有依赖赋值结果的地方都能正确获取结果,但不能保证变量赋值操作的顺序与程序代码中的顺序一致。

比如下面的代码,如果 initialized 没有被 volatile 修饰,那么由于指令重排序优化,赋值语句initialized=true有可能先于配置读取被执行,这就可能导致第二段代码执行出现问题。

volatile boolean initialized = false;

// load config
//...
initialized = true


//下面代码在另一个线程中执行
while(!initialized){
    sleep();
}
doStuffWithConfig();

volatile 禁止重排序的语义在 JDK 1.5 才被修复,之前的版本就算使用 volatile 也不能完全避免指令重排序,即在 JDK 1.5 之前无法通过 DCL 方式实现单例模式。

如何实现可见性和禁止重排序?

在对volatile 修饰的变量进行修改后,会通过增加内存屏障(空操作加lock前缀)的方式,将本CPU缓存写入到内存中,这会导致其他CPU缓存失效,所以其他CPU再次访问这个值时只能重新从内存读取,这就保证了变量在不同线程之间的可见性。

内存屏障将数据同步到内存后,也就意味着内存屏障前的指令都是执行完成的,后面的指令不能再被重排序到内存屏障之前。

上一页线程池下一页AbstractQueuedSynchronizer

最后更新于5年前

这有帮助吗?

还有,如果静态变量没有被volatile修饰,也可能导致类似的问题。

双重检查的单例模式