多线程原理(部分)

volatile

解释

volatile修饰的变量能够保证可见性,但不保证原子性,每个线程能够获取该变量的最新值。

如何保持可见性

对volatile修饰的词,程序在编译的时候,会多一个lock汇编指令如下图所示:
在这里插入图片描述
在这里插入图片描述
该lock指令有两个主要作用:

  • 将当前缓存行的数据回写到内存中
  • 使其他cpu里缓存了该内存地址的数据无效(缓存一致性机制)

JMM主要是通过设置内存屏障来禁止指令重排序,下图是汇编程序中的四种内存屏障类型
在这里插入图片描述

屏障类型说明
LoadLoad Barriers保证Load1数据的装载先于Load2及所有后续指令的装载
StoreStore Barriers保证Store1数据对其他处理器的可见先于Store2及所有后续存储指令的存储
LoadStore Barriers确保Load1数据装载先于Store2及所有后续的存储指令刷新到内存
StoreLoad Barriers确保Store1数据对其他处理器的可见先于Load2及所有后续装载指令的装载

volatile是如何防止指令重排序的:

  • volatile写操作前面插入一个StoreStore屏障
  • volatile写操作后面插入一个StoreLoad屏障
  • volatile读操作后面插入一个LoadLoad屏障
  • volatile读操作后面插入一个LoadStore屏障

适用场景

在使用volatile时需满足一下两个条件:

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

volatile和synchronized

  1. 使用区别:volatile只能修饰变量,synchronized只能修饰方法和语句块
  2. 原子性:synchronized可以保证原子性,volatile不能保证原子性
  3. 可见性:都可以保证可见性,但实现原理不同volatile对变量加了lock,synchronized使用monitorEnter和monitorexit monitor JVM
  4. 有序性:volatile能保证有序,synchronized可以保证有序性,但是代价(重量级)并发退化到串行
  5. 线程阻塞:synchronized会引起线程阻塞,volatitle不会引起线程阻塞

CPU高速缓存

CPU缓存是位于CPU与内存之间的临时存储器,它的容量比内存小的多但是交换速度却比内存要快得多。高速缓存的出现主要是为了解决CPU运算速度与内存读写速度不匹配的矛盾,因为CPU运算速度要比内存读写速度快很多,这样会使CPU花费很长时间等待数据到来或把数据写入内存。在缓存中的数据是内存中的一小部分,但这一小部分是短时间内CPU即将访问的,当CPU调用大量数据时,就可避开内存直接从缓存中调用,从而加快读取速度。
下图为CPU高级缓存层次图:
在这里插入图片描述
在引入CPU告诉缓存后,会伴随着缓存不一致的情况的出现,针对此问题,在CPU层引入:

  • 总线锁
  • 缓存锁

缓存锁的核心是MESI(缓存一致性协议)

MESI

mesi表是缓存的四种状态,分别是:
在这里插入图片描述

Happen-before规则

  • 程序顺序规则,程序代码顺序,书写在前面的操作先行于书写在后面的操作,控制流顺序不是代码顺序还要考虑分支和循环。
  • 管程锁定规则,一个unlock操作一定发生在同一个锁的lock操作之后。
  • volatitle规则,对volatitle操作一定写一定先于后面的读操作
    线程启动规则,start方法先于所有操作
  • 线程终止规则,所有操作都先于线程终止检查
  • 线程中断规则,对线程interrupt方法调用先于线程终止检查到中断事件发生。
  • 对象终结规则,一个对象的初始化完成,先行发生于它的finalize的方法开始。
  • 传递性
  • synchronized监控器锁
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值