Java中同步的记忆效应 [英] Memory effects of synchronization in Java

查看:19
本文介绍了Java中同步的记忆效应的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

JSR-133 常见问题 说:

但还有更多同步比互斥.同步确保内存由线程写入在同步块之前或期间以可预测的方式可见其他线程的方式在同一台显示器上同步.后我们退出一个同步块,我们释放监视器,它具有将缓存刷新到主缓存的效果内存,这样写的线程可以被其他人看到线程.在我们进入一个同步块,我们获取监视器,其作用是使本地处理器缓存无效这样变量将被重新加载从主内存.然后我们就可以查看所有可见的写入之前的版本.

But there is more to synchronization than mutual exclusion. Synchronization ensures that memory writes by a thread before or during a synchronized block are made visible in a predictable manner to other threads which synchronize on the same monitor. After we exit a synchronized block, we release the monitor, which has the effect of flushing the cache to main memory, so that writes made by this thread can be visible to other threads. Before we can enter a synchronized block, we acquire the monitor, which has the effect of invalidating the local processor cache so that variables will be reloaded from main memory. We will then be able to see all of the writes made visible by the previous release.

我还记得读到过,在现代 Sun VM 上,无竞争的同步很便宜.我对这个说法有点困惑.考虑如下代码:

I also remember reading that on modern Sun VMs uncontended synchronizations are cheap. I am a little confused by this claim. Consider code like:

class Foo {
    int x = 1;
    int y = 1;
    ..
    synchronized (aLock) {
        x = x + 1;
    }
}

更新 x 需要同步,但是获取锁是否也从缓存中清除了 y 的值?我无法想象会是这样,因为如果这是真的,像锁条带这样的技术可能无济于事.或者,JVM 能否可靠地分析代码以确保 y 不会在另一个使用相同锁的同步块中被修改,从而在进入同步块时不会在缓存中转储 y 的值?

Updates to x need the synchronization, but does the acquisition of the lock clear the value of y also from the cache? I can't imagine that to be the case, because if it were true, techniques like lock striping might not help. Alternatively can the JVM reliably analyze the code to ensure that y is not modified in another synchronized block using the same lock and hence not dump the value of y in cache when entering the synchronized block?

推荐答案

简短的回答是 JSR-133 的解释太过分了.这不是一个严重的问题,因为 JSR-133 是一个非规范性文档,它不是语言或 JVM 标准的一部分.相反,它只是一个文档,解释了一种足够实现内存模型的可能策略,但通常不是必需.最重要的是,关于缓存刷新"的评论基本上完全不合适,因为基本上零架构将通过执行任何类型的缓存刷新"来实现 Java 内存模型(许多架构甚至没有这样的指令).

The short answer is that JSR-133 goes too far in its explanation. This isn't a serious issue because JSR-133 is a non-normative document which isn't part of the language or JVM standards. Rather, it is only a document which explains one possible strategy that is sufficient for implementing the memory model, but isn't in general necessary. On top of that, the comment about "cache flushing" is basically totally out place since essentially zero architectures would implement the Java memory model by doing any type of "cache flushing" (and many architectures don't even have such instructions).

Java 内存模型是根据可见性、原子性、发生在关系等方面正式定义的,它准确地解释了线程必须看到什么,什么动作必须 使用精确(数学上)定义的模型在其他动作和其他关系之前发生.未正式定义的行为可能是随机的,也可能是在某些硬件和 JVM 实现中在实践中明确定义的 - 但当然你不应该依赖它,因为它可能会在未来发生变化,而且你永远无法真正确定除非您编写了 JVM 并且非常了解硬件语义,否则它首先是明确定义的.

The Java memory model is formally defined in terms of things like visibility, atomicity, happens-before relationships and so on, which explains exactly what threads must see what, what actions must occur before other actions and other relationships using a precisely (mathematically) defined model. Behavior which isn't formally defined could be random, or well-defined in practice on some hardware and JVM implementation - but of course you should never rely on this, as it might change in the future, and you could never really be sure that it was well-defined in the first place unless you wrote the JVM and were well-aware of the hardware semantics.

因此,您引用的文本并未正式描述 Java 保证的内容,而是描述了一些具有非常弱的内存排序和可见性保证的假设架构如何使用缓存满足 Java 内存模型要求冲洗.任何关于缓存刷新、主内存等的实际讨论显然都不适用于 Java,因为这些概念不存在于抽象语言和内存模型规范中.

So the text that you quoted is not formally describing what Java guarantees, but rather is describing how some hypothetical architecture which had very weak memory ordering and visibility guarantees could satisfy the Java memory model requirements using cache flushing. Any actual discussion of cache flushing, main memory and so on is clearly not generally applicable to Java as these concepts don't exist in the abstract language and memory model spec.

在实践中,内存模型提供的保证比完全刷新要弱得多——让每个原子的、与并发相关的或锁定操作刷新整个缓存会非常昂贵——这在实践中几乎从未做过.相反,使用特殊的原子 CPU 操作,有时与 内存屏障 指令结合使用,这有助于确保内存可见性和排序.因此,通过注意到第一个是真的而第二个不是 - Java 内存模型不需要完全刷新(并且在实践中没有发生刷新),从而解决了廉价的无竞争同步和完全刷新缓存"之间的明显不一致.

In practice, the guarantees offered by the memory model are much weaker than a full flush - having every atomic, concurrency-related or lock operation flush the entire cache would be prohibitively expensive - and this is almost never done in practice. Rather, special atomic CPU operations are used, sometimes in combination with memory barrier instructions, which help ensure memory visibility and ordering. So the apparent inconsistency between cheap uncontended synchronization and "fully flushing the cache" is resolved by noting that the first is true and the second is not - no full flush is required by the Java memory model (and no flush occurs in practice).

如果正式的内存模型有点难以消化(您不会孤单),您还可以通过查看 Doug Lea 的食谱,实际上在 JSR-133 常见问题中有链接,但从具体的硬件角度来看问题,因为它适用于编译器编写者.在那里,他们确切地讨论了特定操作所需的障碍,包括同步——那里讨论的障碍可以很容易地映射到实际硬件.大部分实际映射都在说明书中进行了讨论.

If the formal memory model is a bit too heavy to digest (you wouldn't be alone), you can also dive deeper into this topic by taking a look at Doug Lea's cookbook, which is in fact linked in the JSR-133 FAQ, but comes at the issue from a concrete hardware perspective, since it is intended for compiler writers. There, they talk about exactly what barriers are needed for particular operations, including synchronization - and the barriers discussed there can pretty easily be mapped to actual hardware. Much of the actual mapping is discussed right in the cookbook.

这篇关于Java中同步的记忆效应的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆