《深入理解java内存模型》-笔记
- java各线程共享使用主内存,通过共享内存通信
-
重排序
重排序.png
编译器重排序是不影响单线程执行结果的前提下,优化语句执行顺序
指令级重排序是依赖指令并行技术,对无数据依赖性的指令进行重排序并行执行
内存重排序是cpu使用缓存读写,批量写入主内存。应用线程在cpu1写入数据到cpu1的缓存中,然后在cpu2执行时读取数据读取的是cpu2的缓存,此时可能cpu1的缓存未更新到主内存中或cpu2的缓存未从主内存获取最新数据。对应用来说期望是写在读前,实际结果是读在写前,好像是读写内存重排序了。 - 内存屏障
前一内存操作完,后一内存操作才可执行
主要是写读屏障,写之后所有cpu缓存先刷新到主内存中,然后才能读 - happens-before
展现给程序员的是java语句的前后执行关系。
内部通过禁止重排序,设置内存屏障实现。 - 数据依赖
存在数据依赖的语句,指令或内存操作不可被重排序,会影响执行结果。重排序是为了提高程序执行并发度,同时不可更改执行结果。 - JMM不保证完全顺序一致性,在同步锁控制的边界点保证顺序一致,锁内部则会为提高并发效率进行重排序
- 64位long或double数据在32位机器上,应一次总线事务只能读写32位数据,所以64位类型数据读写会分成两次总线事务读写,中间可能有其他线程更改了该数据,即64位数据读写无原子性
一 volatile
1.1 主要概念
-
volatile无法保证自增操作的原子性
++i包含了三个独立的操作:读取count的值,将值加1,然后将计算结果写入count。这是一个“读取 - 修改 - 写入”的操作序列,并且其结果状态依赖于之前的状态。
自增院子性通过AtomicLong等类型,或加同步锁 -
原子性,即使64位也保证原子性。写之后的storeload屏障,读之后的loadstore屏障保证禁止volatile读写之间禁止重排序
-
可见性,读总是读的最新值。storeLoad屏障实现。
写volatile变量时,storeLoad屏障刷新缓存数据到主内存中,使其他cpu缓存失效。
读volatile变量时,重新从主内存中获取最新数据。 -
实现类似于同步锁的效果,写=释放锁,读=获取锁。除storeLoad屏障外添加的其他内存屏障保证实现。
volatile写之前的其他写(操作A),现在在volatile写之前刷新到主内存中。
volatile读之后的读写(操作B)被限制在volatile读完成之后执行.
即操作A完成后通过volatile写通知,volatile读获取通知后执行操作B
通过添加一组内存屏障实现,见1.2节
1.2 实现说明
-
重排序限制
volatile重排序限制.png
-
保守策略使用内存屏障控制重排序
volatile写.png

- 锁能保证一组操作的并发控制,volatile只能保证对当前变量的并发访问。
二 锁
-
volatile int state;
表示锁状态,保证加锁后或释放锁后,其他并发进程实时获取到最新锁状态信息。 - cas更新锁状态,防止并发加锁

三 final
- 通过内存屏障保证读取到的final域的值都是已经构造初始化完成的值。
网友评论