Java中同步的内存效应

Java中同步的内存效应,java,concurrency,jvm,java-memory-model,Java,Concurrency,Jvm,Java Memory Model,说: 但同步还有很多问题 而不是相互排斥。同步 确保内存由线程写入 在同步块之前或期间 以可预测的方式显示 以其他线程的方式 在同一监视器上同步。之后 我们退出一个同步块,我们 释放监视器,它具有 将缓存刷新到主缓存的效果 内存,所以由这个 线程可以对其他线程可见 线程。在我们进入一个 同步块,我们获得 监视器,具有以下效果: 使本地处理器缓存无效 以便重新加载变量 从主存储器。然后我们就可以 要查看使所有写入可见,请执行以下操作: 在上一版本中 我还记得读到过,在现代Sun虚拟机上,无竞争的同

说:

但同步还有很多问题 而不是相互排斥。同步 确保内存由线程写入 在同步块之前或期间 以可预测的方式显示 以其他线程的方式 在同一监视器上同步。之后 我们退出一个同步块,我们 释放监视器,它具有 将缓存刷新到主缓存的效果 内存,所以由这个 线程可以对其他线程可见 线程。在我们进入一个 同步块,我们获得 监视器,具有以下效果: 使本地处理器缓存无效 以便重新加载变量 从主存储器。然后我们就可以 要查看使所有写入可见,请执行以下操作: 在上一版本中

我还记得读到过,在现代Sun虚拟机上,无竞争的同步是便宜的。我对这种说法有点困惑。考虑代码:

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

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

由于y超出了同步方法的范围,因此不能保证对它的更改在其他线程中可见。如果要保证在所有线程中看到对y的更改是相同的,那么所有线程在读/写y时都必须使用同步

如果某些线程以同步方式更改y,而其他线程则不更改y,那么您将获得意外的行为。线程之间共享的所有可变状态必须同步,才能保证看到线程之间的更改。必须同步所有线程对共享可变状态(变量)的所有访问

是的,JVM保证当锁被持有时,没有其他线程可以进入受同一锁保护的代码区域

对x的更新需要同步, 但是锁的获得 同时从中清除y的值 隐藏物我无法想象这是最重要的 因为如果这是真的, 锁条化等技术可能会 没有帮助

我不确定,但我想答案可能是“是的”。考虑这一点:

class Foo {
    int x = 1;
    int y = 1;
    ..
    void bar() {
        synchronized (aLock) {
            x = x + 1;
        }
        y = y + 1;
    }
}
现在,这段代码是不安全的,这取决于程序的其余部分发生了什么。但是,我认为内存模型意味着
bar
看到的
y
值不应早于获取锁时的“真实”值。这意味着缓存必须对
y
x
无效

JVM还可以可靠地分析 确保y未被修改的代码 在另一个同步块中,使用 同一把锁

如果锁是
this
,那么在所有类都预加载后,作为全局优化,该分析看起来是可行的。(我不是说这很容易,也不是说值得……)


在更一般的情况下,证明一个给定的锁只用于一个给定的“拥有”实例的问题可能很难解决。

我们是java开发人员,我们只知道虚拟机,不知道真正的机器

让我从理论上解释一下发生了什么——但我必须说我不知道我在说什么

假设线程A在带缓存A的CPU A上运行,线程B在带缓存B的CPU B上运行

  • 线程A读取y;CPU A从主存中提取y,并将值保存在缓存A中

  • 线程B将新值分配给“y”。VM此时不必更新主内存;就线程B而言,它可以在“y”的局部图像上读/写;也许“y”只是一个cpu寄存器

  • 线程B退出同步块并释放监视器。(它何时何地进入街区并不重要)。线程B已经更新了相当多的变量,包括“y”。所有这些更新必须立即写入主内存

  • CPU B将新的y值写入主内存中的“y”。(我想象)几乎在瞬间,“主y已更新”信息连接到缓存A,缓存A使其自己的y副本无效。这在硬件上一定发生得很快

  • 线程A获取一个监视器并输入一个同步块-此时它不必对缓存A执行任何操作。“y”已经从缓存A中消失。当线程A再次读取y时,它从主存中以B分配的新值刷新

  • 考虑另一个变量z,它在步骤(1)中也被A缓存,但在步骤(2)中它没有被线程B更新。它可以在缓存A中一直存活到步骤(5)。对“z”的访问不会因为同步而减慢

    如果上述说法有道理的话,那么成本确实不是很高



    第(5)步的补充:线程A可能有自己的缓存,甚至比缓存A更快——例如,它可以使用变量“y”的寄存器。这不会在步骤(4)中失效,因此在步骤(5)中,线程A必须在同步输入时擦除自己的缓存。不过这并不是一个巨大的惩罚。

    简单的回答是JSR-133在解释上走得太远了。这不是一个严重的问题,因为JSR-133是一个非规范性文档,它不是语言或JVM标准的一部分。相反,它只是一个文档,解释了一种可能的策略,这种策略足以实现内存模型,但一般来说并不必要。除此之外,关于“缓存刷新”的评论基本上是完全不恰当的,因为基本上零体系结构将通过执行任何类型的“缓存刷新”来实现Java内存模型(许多体系结构甚至没有这样的指令)

    Java内存模型是正式定义的