admin管理员组文章数量:1534204
某一天看书, 发现AtomicInteger里面有个lazySet方法. 很奇怪 不知道是干啥. 所以就有了这篇文章.
更多拾遗系列文章
lazySet方法
源代码:
/**
* Eventually sets to the given value.
*
* @param newValue the new value
* @since 1.6
*/
public final void lazySet(int newValue) {
unsafe.putOrderedInt(this, valueOffset, newValue);
}
// 一般的set方法
/**
* Sets to the given value.
*
* @param newValue the new value
*/
public final void set(int newValue) {
value = newValue;
}
其中value
是volatile类型. 于是看了这篇文章: JUC中Atomic class之lazySet的一点疑惑 里面解释了lazySet和set的区别 就是减少了一个StoreLoad屏障.
为啥会有内存屏障
我们知道cpu和内存之间实际上会有各级缓存
在多线程环境下, 每个CPU都有自己缓存的数据, 那么对于同一数据 可能在多个线程 (有可能在多个核心上执行)就很可能出现数据不一样的情况? 那么我们如何保证这样的程序是正确的或者可预期的呢? 而不是毫无头绪. 所以我们需要一种机制来让别的处理器知道数据可能会过期. 所以就有了下面的几种内存屏障原文:
- LoadLoad屏障:对于这样的语句Load1; LoadLoad; Load2,在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。
- StoreStore屏障:对于这样的语句Store1; StoreStore; Store2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。
- LoadStore屏障:对于这样的语句Load1; LoadStore; Store2,在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。
- StoreLoad屏障:对于这样的语句Store1; StoreLoad; Load2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。它的开销是四种屏障中最大的。在大多数处理器的实现中,这个屏障是个万能屏障,兼具其它三种内存屏障的功能。
————————————————
有这么多种是不是很难记? 实际上就3种, LoadLoad, LoadStore, StoreLoad. 因为StoreStore并不能保证Load操作不被重排序, 所以在cpu中都没有实现. 而在intel cpu中, LoadLoad对应了lfence指令. 这里描述了内存屏障,
保证LoadLoad之前的所有Load操作都已经完成.
LoadStore对应了sfence指令.
保证的是 屏障前面的每个Store对屏障后的每个Store都可见.
StoreLoad对应了mfence.
为啥StoreLoad是最慢的?
这里 和 这里 解释了为啥StoreLoad屏障是最慢的.
从上面的StoreLoad/mfence可以看到, StoreLoad保证 屏障前面的所有Load. Store对屏障后的所有Load,或者Store都可见. – 很明显这个就很强了.
为啥会有重排序?
这里 很好的解释了为啥要重排序, 以及为啥重排序会更快.
References
- volatile使用的lock指令 - http://ifeve/juc-atomic-class-lazyset-que/
- 所有的指令下载地址百度盘
链接: https://pan.baidu/s/1Wah0C3718BFo5idc0dQaWw 提取码: nmux 复制这段内容后打开百度网盘手机App,操作更方便哦
版权声明:本文标题:Java拾遗2 - volatile和内存屏障和cpu指令 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/xitong/1726873194a1088063.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论