JMM、Volatile(Juc-10)

本贴最后更新于 1480 天前,其中的信息可能已经东海扬尘

概述

谈谈你对 volatile 的理解

  1. 保证可见性
  2. 不保证原子性
  3. 禁止指令重排

什么是 JMM

java 内存模型,是一个不存在的东西,是一个概念,一个约定!

关于 JMM 一些同步的约定:

  1. 线程解锁前一定把共享变量立刻刷回主储存
  2. 线程加锁钱必须把主存最新值 copy 到工作内存中
  3. 加锁一定是同一把锁

线程,工作内存,主内存一共有 4 组 8 种操作

image.png

内存交互操作

  内存交互操作有 8 种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于 double 和 long 类型的变量来说,load、store、read 和 write 操作在某些平台上允许例外)

    • lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态
    • unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
    • read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的 load 动作使用
    • load (载入):作用于工作内存的变量,它把 read 操作从主存中变量放入工作内存中
    • use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令
    • assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中
    • store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的 write 使用
    • write  (写入):作用于主内存中的变量,它把 store 操作从工作内存中得到的变量的值放入主内存的变量中

  JMM 对这八种指令的使用,制定了如下规则:

    • 不允许 read 和 load、store 和 write 操作之一单独出现。即使用了 read 必须 load,使用了 store 必须 write
    • 不允许线程丢弃他最近的 assign 操作,即工作变量的数据改变了之后,必须告知主存
    • 不允许一个线程将没有 assign 的数据从工作内存同步回主内存
    • 一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是怼变量实施 use、store 操作之前,必须经过 assign 和 load 操作
    • 一个变量同一时间只有一个线程能对其进行 lock。多次 lock 后,必须执行相同次数的 unlock 才能解锁
    • 如果对一个变量进行 lock 操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新 load 或 assign 操作初始化变量的值
    • 如果一个变量没有被 lock,就不能对其进行 unlock 操作。也不能 unlock 一个被其他线程锁住的变量
    • 对一个变量进行 unlock 操作之前,必须把此变量同步回主内存

  JMM 对这八种操作规则和对 volatile 的一些特殊规则就能确定哪里操作是线程安全,哪些操作是线程不安全的了。但是这些规则实在复杂,很难在实践中直接分析。所以一般我们也不会通过上述规则进行分析。更多的时候,使用 java 的 happen-before 规则来进行分析。

问题

程序不知道主内存的值已经被修改过了,类似这样的代码就会出现死循环

volatile
  1. 保证可见性

  • 不保证原子性
  • 原子性:不可分割

    也就是线程 A 在执行任务的时候是不能被打扰和被分割的,要么同时成功,要么同时失败

    方案:使用原子类,解决原子性问题 AtomicInteger

    AtomicInteger 他的底层用的是 CAS

    • JUC
      17 引用 • 3 回帖 • 1 关注

    相关帖子

    欢迎来到这里!

    我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

    注册 关于
    请输入回帖内容 ...