并发编程 - volatile内存语义

可见性: 关键就是保证load、use的执行顺序不被打乱(保证使用变量前一定进行了load操作,从主存拿最新值来),assign、wirte的执行顺序不被打乱(保证赋值后马上就是把值写到主存),所以使用内存屏障
有序性: Java 内存模型具备一些先天的“有序性”,即不需要通过任何手段就能够得到保证的有序性,这个通常也称为 happens-before 原则。
volatile 变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作。

volatile 特性

可以把对volatile 变量的单个读/写,看成是使用同一个锁对这些单个读/写操作做了同步
在这里插入图片描述
可以看成
在这里插入图片描述

可见性: 对一个volatile 变量的读,总是能看到(任意线程)对这个volatile变量最后的写入。
原子性: 对任意单个volatile 变量的读/写具有原子性,但类似于volatile++这种复合操作不具有原子性。

volatile 的内存语义

内存语义:可以简单理解为volatile,synchronize,atomic,lock 之类的在JVM中的内存方面实现原则。

volatile 写的内存语义如下: 当写一个volatile 变量时,JMM 会把该线程对应的本地内存中的共享变量值刷新到主内存。
volatile 读的内存语义如下: 当读一个volatile 变量时,JMM 会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。
在这里插入图片描述
在这里插入图片描述

为何volatile 不是线程安全的

在这里插入图片描述

volatile 内存语义的实现

volatile 重排序规则表
在这里插入图片描述
当第二个操作是volatile 写时,不管第一个操作是什么,都不能重排序。这个规则确保volatile 写之前的操作不会被编译器重排序到volatile 写之后。

当第一个操作是volatile 读时,不管第二个操作是什么,都不能重排序。这个规则确保volatile 读之后的操作不会被编译器重排序到volatile 读之前。

当第一个操作是volatile 写,第二个操作是volatile 读时,不能重排序。

volatile 的内存屏障
在Java 中对于volatile 修饰的变量,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序问题。
在这里插入图片描述
storestore 屏障:对于这样的语句store1; storestore; store2,在store2 及后续写入操作执行前,保证store1 的写入操作对其它处理器可见。(也就是说如果出现storestore 屏障,那么store1 指令一定会在store2 之前执行,CPU 不会store1与store2 进行重排序)

storeload 屏障:对于这样的语句store1; storeload; load2,在load2 及后续所有读取操作执行前,保证store1 的写入对所有处理器可见。(也就是说如果出现storeload 屏障,那么store1 指令一定会在load2 之前执行,CPU 不会对store1 与load2 进行重排序)
在这里插入图片描述
在每个volatile 读操作的后面插入一个LoadLoad 屏障。在每个volatile 读操作的后面插入一个loadstore 屏障。

loadload 屏障:对于这样的语句load1; loadload; load2,在load2 及后续读取操作要读取的数据被访问前,保证load1 要读取的数据被读取完毕。(也就是说,如果出现loadload 屏障,那么load1 指令一定会在load2 之前执行,CPU不会对load1 与load2 进行重排序)

loadstore 屏障:对于这样的语句load1; loadstore; store2,在store2 及后续写入操作被刷出前,保证load1 要读取的数据被读取完毕。(也就是说,如果出现loadstore 屏障,那么load1 指令一定会在store2 之前执行,CPU 不会对load1 与store2 进行重排序)

volatile 的实现原理

通过对OpenJDK 中的unsafe.cpp 源码的分析,会发现被volatile 关键字修饰的变量会存在一个“lock:”的前缀。

Lock 前缀,Lock 不是一种内存屏障,但是它能完成类似内存屏障的功能。Lock会对CPU 总线和高速缓存加锁,可以理解为CPU 指令级的一种锁。

同时该指令会将当前处理器缓存行的数据直接写会到系统内存中,且这个写回内存的操作会使在其他CPU 里缓存了该地址的数据无效。

在具体的执行上,它先对总线和缓存加锁,然后执行后面的指令,最后释放锁后会把高速缓存中的脏数据全部刷新回主内存。在Lock 锁住总线的时候,其他CPU 的读写请求都会被阻塞,直到锁释放。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值