volatile

volatile:线程访问共享变量,通过排它锁单独获得该变量
volatile=轻量级synchronized:不会引起线程上下文切换调度,使用执行成本低

原理

操作系统知识
volatile修饰的变量写操作时会在前面加LOCK前缀≈内存屏障

  • 将当前缓存行的数据写回内存
    LOCK锁缓存不锁总线(锁总线,阻塞其他cpu访问内存,开销大)
    缓存锁定:如果访问的内存区域已经缓存在处理器内部,则锁定该内存的缓存,并写回到内存,使用缓存一致性机制保证修改原子性
    缓存一致性机制:阻止同时修改由两个以上处理器缓存的内存数据区域
  • 其他cpu(多处理器)缓存了该内存地址的数据无效
    处理器嗅探总线判断数据是否有效(其他处理器打算写内存地址&该内存地址处于共享状态,则无效),无效则设为无效重新缓存

∴volatile直接存于主存,易变,不稳定

内存屏障:为了性能,编译器和处理器会对指令进行重排序,内存屏障是一条指令,对指令重排做出一定限制。指令重排只能保证在单线程执行下逻辑正确,多线程可能会出错

特性

  • 可见性:变量在线程间可见性,基于cpu内存屏障指令
boolean flag = true;
while(flag){
	xxx
}
flag = false;
/*
JMM:主存共享,线程栈(工作内存)线程私有
线程1:修改flag(线程栈中),还未写回主存,转去做其他事
线程2:不知道,一直循环
*/
  • 顺序性:禁止指令重排序,效率↓
    volatile前的语句执行完且对后可见
    volatile后的语句不能重排到volatile前

    • 编译期:JMM遵循内存屏障约束
    StoreStoreLoadLoad
    volatile变量写volatile变量读
    StoreLoadLoadStore
    • 运行期:依靠cpu屏障指令
  • 原子性:与CAS结合保证原子性,volatile本身不保证操作原子性。
    只能单独的读或者写,读+写 之间是可以被中断的,这意味着在读取或者修改 volatile 变量的过程中,其他线程可能会对这个变量进行修改。

  • 线程安全:volatile不能保证操作的原子性,所以并不是线程安全的 eg:i++;

    当满足以下条件时volatile是线程安全的

    • 对变量的写操作不依赖于当前值
    • 该变量没有包含在具有其他变量的不变式中

内存语义

  • 写volatile变量,JMM将该线程对应的本地内存中的共享变量刷新到主内存
  • 读volatile变量,JMM将该线程对应的本地内存置为无效,从主内存中读取共享变量

总结:

  • 写volatile变量,实质上是告诉其他读该变量的线程,要修改共享变量
  • 读volatile变量,实质上是接收了其他线程修改共享变量的消息
  • 线程a写,线程b读,实质上是a通过主内存向b发送消息

实现:

在这里插入图片描述
第二个操作是volatile写时,第一个操作不管是什么,都不能重排序
第一个操作是volatile读时,第二个操作不管是什么,都不能重排序
第一个操作是volatile是写,第二个操作是volatile是读,不能重排序

Load:加载(读)、Store:保存(写)
JMM内存屏障插入策略:
在每个volatile写操作前插入StroreStore屏障
在每个volatile写操作前插入StroreLoad屏障
在每个volatile读操作前插入LoadLoad屏障
在每个volatile读操作前插入LoadStore屏障

优化

一般高速缓存行64字节宽(有的32),不支持部分填充
共享变量不足64字节,则与其他数据共同存在于一个高速缓存行。
多cpu,每个处理器都会缓存同样的数据
共享变量频繁写,则频繁锁定当前缓存行,其他处理器数据失效。
∴补足64字节(高速缓存行的长度)独占一个高速缓存行。锁定时不影响其他数据
注:java7可能不生效,会淘汰/重新排序无用字段

volatile与synchronized

volatilesynchronized
修饰变量方法、代码块、类对象
性能线程同步的轻量级实现。volatile=轻量级synchronized:不会引起线程上下文切换调度,使用执行成本低
多线程不阻塞阻塞
三大特性保证可见、有序,不保证原子保证可见、有序、原子
同步变量在主存和工作内存间同步多线程访问资源同步
优化编译器优化

volatile和CAS底层实现都用CPU的lock指令,有什么不同?

  • lock是前缀,后面跟命令,具体看后面的命令
  • volatile不保证原子性,实现需要内存屏障,lock前缀的指令具有内存屏障的效果,作内存屏障使用的。
  • CAS保证原子性,CAS的实现用了lock cmpxchg指令。cmpxchg指令涉及一次内存读和一次内存写,需要lock前缀保证中间不会有其它cpu写这段内存。

lock:执行时,会设置处理器的LOCK#信号(这个信号会锁定缓存,阻止其它CPU修改,直到这些指令执行结束),这条指令的执行变成原子操作,之前的读写请求都不能越过lock指令进行重排,相当于一个内存屏障。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值