JMM内存模型和Volatile

什么是JMM

  1. Java定义的**并发编程操作的一组规范**,除了抽象出**主内存与线程之间的关系**以外,还规定了**从Java代码到Cpu指令转换过程**的规定。
  2. 制定这些规定是为了**简化程序员在并发环境下的开发**,因为Cpu多级缓存的模型和指令重排序的优化会影响并发编程下的结果,通过制定相关的规范,程序员可以通过实现了相关规范的关键字例如(Synchronized、Volatile等)从而更方便的写出安全的程序。

内存模型图

  1. 程序不会直接操作主内存,会先将共享变量拷贝到线程的本地内存中,进行修改,再将修改后的共享变量副本刷入到主内存中。
  2. JMM负责定义主内存和线程之间的操作.
  3. 线程之间无法直接进行数据的交换,必须将数据写入到主内存中,其他线程从主内存进行读取的方式来进行线程间的数据交换。

image.png

JMM数据原子操作

  1. read(读取):作用于主内存中的变量,从主内存中读取数据
  2. load(载入):将从read操作中读取的数据加载到本地内存中
  3. use(使用):工作线程读取本地内存中的值
  4. assign(赋值):工作线程将处理后的值赋值到本地内存中
  5. store(存储):将本地内存中的数据写入到主内存中
  6. write(写入):将store入主内存中的数据写入到共享变量中
  7. lock(加锁):将主内存变量标记为线程独占状态
  8. unlock(解锁):将主内存变量从线程独占状态解除

并发编程三大特性

可见性

有序性

原子性

Happens-before和as-if-serial

Happens-before

  • JMM提供**Happens-before**原则,在**多线程环境**下也能够保证提供保证**有序性****可见性**
  • 定义为如果线程A与线程B中存在**Happens-before**关系,则线程A所做的操作 线程B可见,并且线程A的操作在线程B的操作之前执行。

Happens遵守的规则

  • **程序顺序规则**:一个线程中的每个操作,happens-before于该线程中的任意后续操作。
  • **监视器锁规则**:对一个锁的解锁,happens-before于随后对这个锁的加锁。
  • **volatile变量规则**:对一个volatile域的写,happens-before于任意后续对这个volatile域的读。
  • **传递性规则**:如果A happens-before B,且B happens-before C,那么A happens-before C。
  • **start()规则**:如果线程A执行操作ThreadB.start()(启动线程B),那么A线程的ThreadB.start()操作happens-before于线程B中的任意操作。
  • **join()规则**:如果线程A执行操作ThreadB.join()并成功返回,那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回。

as-if-serial

制定规则,无论通过任何规则的重排序,需要保证在**单线程**环境下,重排序后的代码执行结果与重排序前相等。

Synchronized

Synchronized获取锁的过程

  1. 获取锁
  2. 清空本地内存
  3. 将主内存中的变量拷贝到本地内存中
  4. 执行代码块
  5. 将本地内存中的变量刷新到主存中
  6. 释放锁
  7. Synchronized会在代码块前后添加内存屏障,保证代码块的内容不会重排序

原子性

  1. 在添加了Synchronized后,会在代码的前后增加**monitorenter****monitorexit**
  2. 从而在执行代码块时需要**先获取到锁**
  3. 因为在同一个时间段内**只能有一个线程**获取到锁,因此**同一时间段内只会有一个线程**执行代码块,从而能够保证原子性

有序性

  1. Synchronized不能够保证线程内的不进行指令重排序
  2. 但因为Synchronized确保了代码块只能由一个线程执行,而指令重排序的**as if serial**原则确保了,在单线程环境下不改变结果,因此Synchronized能够保证有序性

可见性

  1. **释放锁**前会将本地内存中的变量**刷入到主内存**中,保证其他线程能够读取到最新的数据
  2. **获取锁前**,会**先清空本地内存**中的数据,保证读取到的都是主内存中最新的数据
  3. Synchronized只能够保证,获取**同一个Monitor锁**的线程的可见性,对于不是同一个Monitor锁的线程**不能**够保证可见性

image.png

Volatile

image.pngimage.png

可见性

volatile做了什么事
  • **volatile**修饰的变量,在线程A修改完后,会**第一时间刷新到主内存**
  • 线程BCD在读取该变量时,会通过**读写栅栏**(汇编lock关键字)的方式,来起到从主内存直接读取变量的效果
  • 读写栅栏是一条CPU指令,作用是保证指令的有序性,不会出现重排序现象,同时保证该指令前的写操作能够顺利刷入到主内存中
可见性原理
  • Volatile的底层是通过**C语言**实现的,在底层的**汇编语言**中,在对**Volatile关键字修饰的变量**操作时,会在前加入 **lock**汇编指令。Lock汇编指令能够保证读取Volatile修饰的变量值时,读取到的是最新的值
  • lock 汇编指令的实现原理是在硬件层面上的**缓存一致性协议**
缓存一致性协议(MESI)
  • 当线程修改了**本地内存**中的变量后,需要马上将修改后的变量写入到**主内存**
  • CPU和主内存之间的所有数据交互都需要经过**BUS消息总线**
  • 其他的线程通过 **总线嗅探机制** 监听到数据发生变化,从而让自己线程中的**本地内存中的变量失效**
  • 因此其他线程在主内存的数据被修改后,都会重新到主内存中读取数据,因此读取到的都是新的数据

有序性

Java规范定义的内存屏障

image.png

JVM解析字节码时对Volatile变量的操作
  • volatile变量在**读操作**时,会调用 **OrderAccess::load_acquire****acquire**操作是**LoadLoad****LoadStore**的结合
  • load_acquire可以看成先 **load** 命令读取,再添加 **LoadLoad****LoadStore**屏障
  • volatile变量在**写操作**时,会调用 **OrderAccess::release_store****release**操作是**LoadStore****StoreStore**的结合。因此**release_store**可以看成是,先添加**LoadStore****StoreStore**屏障,再**store**写入主内存
  • 在后面还调用了 **OrderAccess::storeload** ,也就是在**store**命令写入主内存后,还添加了**StoreLoad**屏障
  • 应当后面有Volatile变量读时才需要加入StoreLoad,后面添加StoreLoad屏障是为了保险

if(属性读指令操作){
     if(属性被volatile修饰){
         OrderAccess::load_acquire
     }else{
         正常读
     }
     ......
}else if(属性写指令操作){
    if(属性被volatile修饰){
        OrderAccess::release_store
        OrderAccess::storeload
    }else{
        正常写
    }
}

汇编对内存屏障的实现
  • 只有**多核CPU**才会添加内存屏障,单核CPU不会也不需要添加内存屏障,因为没有多线程操作
  • 内存屏障中,只有**StoreLoad**有具体实现,底层添加的是**lock**指令,因此在汇编中有**lock前缀**,可以等价于是添加了**StoreLoad**屏障
  • 其他的屏障只有默认实现,实际是memory指令,表明的是编译屏障,表明编译器不能将memory前后的指令混合在一起
inline void OrderAccess::storeload()  { fence(); }
inline void OrderAccess::fence() {
  //如果是多核CPU(multi-processing)
  if (os::is_MP()) {
    // always use locked addl since mfence is sometimes expensive
#ifdef AMD64
    __asm__ volatile ("lock; addl $0,0(%%rsp)" : : : "cc", "memory");
#else
    __asm__ volatile ("lock; addl $0,0(%%esp)" : : : "cc", "memory");
#endif
  }
}

inline void OrderAccess::loadload()   { compiler_barrier(); }
inline void OrderAccess::storestore() { compiler_barrier(); }
inline void OrderAccess::loadstore()  { compiler_barrier(); }
inline void OrderAccess::storeload()  { fence();            }

inline void OrderAccess::acquire()    { compiler_barrier(); }
inline void OrderAccess::release()    { compiler_barrier(); }
static inline void compiler_barrier() {
  __asm__ volatile ("" : : : "memory");
}

原子性

Volatile不能保证原子性

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值