kRs*_*kRs 5 java multithreading volatile memory-barriers
这更像是一个理论问题.我不确定所有的概念,编译器行为等是否都是最新的并且仍然在使用,但我想确认我是否正确理解了我正在努力学习的一些概念.
语言是Java.
从我到目前为止所理解的,在X86架构上,StoreLoad障碍(尽管用于实现它们的确切的CPU指令)是在Volatile写入之后放置的,以使其在其他线程中的后续易失性读取中可见(因为x86不保证较新的读取总是看到较旧的写入)(参考http://shipilev.net/blog/2014/on-the-fence-with-dependencies/)
现在从这里(http://jpbempel.blogspot.it/2013/05/volatile-and-memory-barriers.html)我看到:
public class TestJIT
{
private volatile static int field1;
private static int field2;
private static int field3;
private static int field4;
private static int field5;
private volatile static int field6;
private static void assign(int i)
{
field1 = i << 1; // volatile
field2 = i << 2;
field3 = i << 3;
field4 = i << 4;
field5 = i << 5;
field6 = i << 6; // volatile.
}
public static void main(String[] args) throws Exception
{
for (int i = 0; i < 10000; i++)
{
assign(i);
}
Thread.sleep(1000);
}
}
Run Code Online (Sandbox Code Playgroud)
生成的程序集仅在field6赋值后才具有StoreLoad,而不是在field1赋值之后,但是它也是volatile.
我的问题:
1)到目前为止我写的是否有意义?还是我完全曲解了什么?
2)为什么编译器在field1 volatile赋值后省略了StoreLoad?这是优化吗?但它有一些缺点吗?例如,在field1赋值后踢入的另一个线程,即使它已被实际更改,仍可能读取field1的旧值?
1)到目前为止我写的东西有意义吗?还是我完全误解了什么?
我认为你做对了一切。
2) 为什么编译器在 field1 volatile 赋值后会忽略 StoreLoad?这是优化吗?但它有一些缺点吗?
是的,这是一种优化,但要做到正确是一个非常棘手的问题。
Doug Lea的JMM食谱实际显示的两个连续的情况下,所推荐的障碍的实例volatile存储和有有一个StoreLoad年代后它们中的每一个StoreStore的两个店和一之间(86无操作)StoreLoad只有在第二个。然而,Cookbook 指出,相关分析可以相当参与。
编译器应该能够证明volatile在 write tofield1和 write to之间的同步顺序中不会发生读取field6。我不确定这是否可行(通过当前的 HotSpot JIT)如果TestJIT稍微改变volatile一下,以便同时在另一个线程中执行相当数量的加载。
例如,另一个线程在 field1 赋值后启动,即使它实际上已被更改,它是否仍可能读取 field1 的旧值?
如果volatile加载按照volatile同步顺序跟随存储,则不应允许发生这种情况。所以如上所述,我认为 JIT 可以逃脱它,因为它没有看到任何volatile负载正在完成。
更新
更改了 JMM Cookbook 示例的详细信息,因为kRs指出我将 a 误认为StoreStore是StoreLoad. 答案的本质根本没有改变。
为什么编译器在 field1 易失性赋值后省略 StoreLoad?
仅要求第一次加载和最后一次存储是易失性的。
这是一种优化吗?
如果发生这种情况,这是最有可能的原因。
但它有一些缺点吗?
只不过你靠的是有两家店的一个屏障。即您需要field1先查看更改field6,而不是偶然发生的变化。
即使 field1 实际上已更改,仍可能读取它的旧值吗?
是的,尽管您无法确定是否发生了这种情况,但您是否希望看到新值,即使其他字段可能尚未设置。
| 归档时间: |
|
| 查看次数: |
346 次 |
| 最近记录: |