我想知道JVM在使用同步时如何保证成员变量修改在引用对象中的可见性。
我知道同步和易失性将为变量修改提供可见性。
class Test{
public int a=0;
public void modify(){
a+=1;
}
}
//Example:
// Thread A:
volatile Test test=new Test();
synchronized(locker){
test.modify();
}
// then thread B:
synchronized(locker){
test.modify();
}
// Now, I think test.a==2 is true. Is it ok? How JVM implements it?
// I know the memory barrier, does it flush all cache to main storage?线程A首先在sychronized块中调用修改,然后将对象传递给线程B(将引用写入volatile变量)。然后线程B再次调用修改(在synchronized中)。
a==2有保证吗?JVM是如何实现的呢?
发布于 2019-04-19 03:37:04
为了你(还不完整!)例如,如果我们可以假设如下:
test中的代码保证在线程B使用之前运行。locker变量包含对线程A& B的同一个对象的引用。然后,我们可以证明,在您指出的点上,a == 2是正确的。如果不保证前提条件1,则线程B可以得到NPE。如果不能保证先决条件2(即线程A和B可以在不同的对象上同步),那么就没有适当的发生--在确保线程B在a上看到线程A的操作结果之前。
(@NathanHughes评论说,volatile是不必要的。我不一定同意这一点。这取决于你的例子的细节,你还没有向我们展示。)
JVM是如何实现它的?
实际的实现是Java平台,(理论上)是特定于版本的。JVM规范内存模型对遵守“规则”的程序的行为设置了约束。这完全是具体的实现,具体是如何发生的。
我知道内存屏障,它会把所有的缓存都刷新到主存储器吗?
这也是具体的实现。有不同类型的记忆障碍,以不同的方式工作。JIT编译器将发出本机代码,这些代码使用适当的指令来满足JLS所要求的保证。如果有一种方法可以在不进行完全缓存刷新的情况下完成这一操作,那么实现可能会这样做。
(有一个JVM命令行选项可以告诉JIT编译器输出本机代码。如果你真的想知道引擎盖下面发生了什么,那是个开始寻找的好地方。
但是,如果您试图理解/分析应用程序的线程安全,则应该使用Java内存模型。此外,使用更高级别的并发抽象,使您能够避免较低级别的陷阱。
发布于 2019-04-18 15:11:40
线程之间的可见性由记忆屏障/栅栏强制执行。如果是synchronized块,JVM将在块执行完成后插入一个内存屏障。
JVM用CPU指令实现内存屏障,例如,用sfence来设置存储屏障,在x86上使用lfence指令来设置加载屏障。还有mfence,可能还有其他指令,可以是特定于CPU体系结构的。
https://stackoverflow.com/questions/55748270
复制相似问题