![]() |
1
41
简短的回答是 JSR-133的解释太离谱了 . 这不是一个严重的问题,因为JSR-133是一个非规范性文档,它不是语言或JVM标准的一部分。相反,它只是一个文档,解释了一种可能的策略,即 足够的 用于实现内存模型,但通常不是 必要的 . 除此之外,关于“缓存刷新”的评论基本上完全不存在,因为本质上零架构将通过执行任何类型的“缓存刷新”来实现Java内存模型(并且许多架构甚至没有这样的指令)。 Java内存模型是根据可见性、原子性、关系发生之前等形式来定义的,它确切地解释了什么线程。 必须 看什么,什么行动 必须 在使用精确(数学)定义的模型进行其他操作和其他关系之前发生。未被正式定义的行为可能是随机的,或者在某些硬件和JVM实现的实践中被很好地定义——但是当然,您不应该依赖于这一点,因为它在将来可能会发生变化,而且除非您编写了JVM并且很好地意识到硬件语义,否则您永远无法真正确定它在一开始就被很好地定义了。抽搐。 因此,您引用的文本并没有正式描述Java所保证的内容,而是描述了一些假设性架构如何具有非常弱的内存排序和可见性保证。 能够 使用缓存刷新来满足Java内存模型的要求。任何对缓存刷新、主内存等的实际讨论显然都不适用于Java,因为这些概念不存在于抽象语言和内存模型规范中。 实际上,内存模型提供的保证要比完全刷新弱得多——让每个原子的、与并发性相关的或锁操作刷新整个缓存将是非常昂贵的——而且这在实践中几乎从未实现过。相反,使用特殊的原子CPU操作,有时与 memory barrier 指令,有助于确保内存的可见性和顺序。因此,廉价的无争用同步和“完全刷新高速缓存”之间的明显不一致是通过注意第一个是真的,第二个不是——Java内存模型不需要完全刷新(并且在实践中没有发生刷新)。 如果正式的记忆模型有点太重而无法消化(你不会孤单一人),你也可以通过看一眼深入研究这个主题。 Doug Lea's cookbook 它实际上在JSR-133 FAQ中链接,但从具体的硬件角度来看,它是针对编译器编写者的。在那里,他们确切地讨论了特定操作所需的屏障,包括同步——这里讨论的屏障可以很容易地映射到实际的硬件上。很多实际的映射都在食谱中讨论过。 |
![]() |
2
9
BeeOnRope是正确的,您引用的文本更多地涉及到典型的实现细节,而不是Java内存模型确实保证的细节。实际上,当您在x上同步时,您经常会看到y实际上是从CPU缓存中清除的(同样,如果示例中的x是一个易失性变量,在这种情况下,不需要显式同步来触发效果)。这是因为在大多数CPU上(请注意,这是硬件效果,而不是JMM描述的效果),缓存在称为缓存线的单元上工作,缓存线通常比机器字长(例如64字节宽)。由于缓存中只能加载或失效完整的行,因此X和Y很有可能落在同一行中,刷新其中一行也会刷新另一行。 可以编写一个显示这种效果的基准。用两个volatile int字段创建一个类,让两个线程执行一些操作(例如,在长循环中递增),一个在一个字段上,另一个在另一个字段上。给手术计时。然后,在两个原始字段之间插入16个int字段,并重复测试(16*4=64)。请注意,数组只是一个引用,因此16个元素的数组不会起作用。您可能会看到性能的显著提高,因为一个字段上的操作将不再影响另一个字段。这是否适用于您将取决于JVM实现和处理器体系结构。我在Sun JVM和一台典型的X64笔记本电脑上看到过这种情况,性能上的差异是好几次的。 |
![]() |
3
7
我不确定,但我想答案可能是“是”。考虑一下:
现在这个代码是不安全的,这取决于程序的其余部分发生了什么。然而,我认为记忆模型意味着
如果锁是
在更一般的情况下,证明一个给定的锁只与一个给定的“拥有”实例一起使用的问题可能是难以解决的。 |
![]() |
4
4
我们是Java开发者,我们只知道虚拟机,而不是真正的机器! 让我理论化正在发生的事情——但我必须说我不知道我在说什么。 假设线程A在带有缓存A的CPU A上运行,线程B在带有缓存B的CPU B上运行,
考虑另一个变量z,它也被步骤(1)中的a缓存,但它没有被步骤(2)中的线程b更新。它可以在缓存A中一直存活到步骤(5)。由于同步,对“z”的访问没有减慢。 如果上述说法有道理的话,那么成本确实不是很高。 除了步骤(5):线程A可能有自己的缓存,甚至比缓存A快-例如,它可以使用变量“y”的寄存器。这不会在步骤(4)中失效,因此在步骤(5)中,线程A必须在进入同步时清除自己的缓存。不过,这不是一个巨大的惩罚。 |
![]() |
5
3
您可能需要检查JDK6.0文档 http://java.sun.com/javase/6/docs/api/java/util/concurrent/package-summary.html#MemoryVisibility 内存一致性属性 Java语言规范的第17章定义了在内存操作(如共享变量的读写)之前发生的关系。只有在写操作发生在读操作之前,一个线程的写操作结果才保证对另一个线程的读操作可见。同步和易失性构造以及thread.start()和thread.join()方法可以在关系之前形成。特别地:
因此,正如上面突出显示的点所述:在监视器上发生解锁之前发生的所有更改对所有锁定的线程(以及在那里自己的同步块)都是可见的。 同一个监视器。这与Java在语义之前发生的情况是一致的。 因此,当其他线程在“alock”上获取监视器时,对y所做的所有更改也将刷新到主内存中。 |
![]() |
6
1
同步保证只有一个线程可以输入一个代码块。但它不能保证在同步部分内所做的变量修改对其他线程是可见的。只有进入同步块的线程才能保证看到更改。 Java中的同步内存效应可与C++和Java双重检查锁定问题进行比较 双校验锁是在多线程环境中实现延迟初始化的一种有效方法,被广泛引用。不幸的是,它 在Java中实现时,不会以平台无关的方式可靠地工作 ,而不进行其他同步。当以其他语言(如C++)实现时,它取决于处理器的内存模型、编译器执行的重新排序以及编译器和同步库之间的交互。由于这些语言中没有一种是用C++语言来指定的,所以很少有人会谈到它将工作的情况。显式内存障碍可以用来使它在C++中工作,但是这些障碍在Java中是不可用的。 |
![]() |
7
-1
由于y超出了synchronized方法的范围,因此无法保证它的更改在其他线程中可见。如果要确保在所有线程中看到对y的更改都是相同的,那么 全部的 线程在读/写Y时必须使用同步。 如果某些线程以同步方式更改y,而其他线程不更改y,那么您将得到意外的行为。必须同步线程之间共享的所有可变状态,才能保证看到线程之间的更改。必须同步所有线程上对共享可变状态(变量)的所有访问。 是的,JVM保证在持有锁的同时,没有其他线程可以进入受同一锁保护的代码区域。 |
![]() |
Sam · jvm如何优化循环代码? 6 年前 |
![]() |
pushpavanthar · 纱线中的调谐火花作业 6 年前 |
![]() |
Ashish K · 在Groovy中将字符串参数传递给对象方法参数 6 年前 |
![]() |
St.Antario · 在Java中侦听多个目录以创建文件 6 年前 |
![]() |
Bonsaisteak · 为什么年轻一代需要三个区域来收集垃圾? 6 年前 |