问题标签 [java-memory-model]

For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.

0 投票
4 回答
541 浏览

java - Java同步对双重检查锁定的影响?

我已经阅读了不同的文章,例如Double-checked locking: Clever, but broken并且我理解以下代码在多线程使用中被破坏的原因。

然而,根据它的解释,当一个线程退出一个同步块时,它会执行一个写屏障——它必须在释放锁之前将该块中修改的所有变量刷新到主内存。因此,当线程 A 运行到同步块时,然后依次执行以下过程:

  1. 将为新的 Resource 对象分配内存;
  2. 将调用 Resource 的构造函数,
  3. 初始化新对象的成员字段;
  4. SomeClass 的字段资源将被分配对新创建对象的引用

最后,在线程 A 退出同步块之前,它会将其本地资源对象写回主存,然后线程 B 将在运行到同步块时从主内存中读取这个新创建的资源。

为什么线程 B 看到这些内存操作的顺序可能与线程 A 执行的顺序不同?我认为线程 B 不会知道资源对象已创建,直到线程 A 从同步块退出时将其本地内存刷新到主内存,因为线程 B 只能从可共享的主内存中读取资源对象?

请纠正我的理解......谢谢。

0 投票
1 回答
893 浏览

java - 当我们说一个特定的数据结构是缓存友好的时,这意味着什么?

我经常读到链表数据结构及其变体跳过列表在并行硬件中是缓存友好的。这是什么意思 ?有人可以用通俗易懂的方式解释一下。

编辑:上下文在 这个链接中。

0 投票
1 回答
218 浏览

java - 声明两个字段 volatile 就足够了吗?

我现在正在编写一个Java ME应用程序。据我所知,它使用旧的Java 内存模型,因为它的功能仅限于 Java 1.3。该模型提供的关于 volatile 关键字的唯一保证是所有读取和写入都直接通过主内存而不是缓存。

考虑以下代码:

从不同的线程调用方法。方法makeBill()写入volatile变量,方法cancelBillRequest()从该字段读取。

声明两个字段volatile 就足够了吗?您对 volatile读写重新排序有什么看法?我的代码安全吗?

0 投票
3 回答
510 浏览

java - 为什么 Java 内存模型允许这种行为?

JMM 中的因果关系似乎是其中最令人困惑的部分。我有几个关于 JMM 因果关系的问题,以及并发程序中允许的行为。

据我了解,当前的 JMM 始终禁止因果循环。(我对吗?)

现在,根据JSR-133文档,第 24 页,图 16,我们有一个示例,其中:

最初x = y = 0

线程 1:

线程 2:

直觉上,r1 = r2 = r3 = 42似乎是不可能的。但是,它不仅被提及为可能,而且在 JMM 中也是“允许的”。

对于这种可能性,我无法理解的文件中的解释是:

编译器可以确定唯一分配给的值x是 0 和 42。据此,编译器可以推断出,在我们执行 的点,我们r1 = x刚刚执行了 42 的写入 x,或者我们刚刚阅读x并看到值 42。在任何一种情况下,读取值 42 都是合法的x。然后它可以更改r1 = xr1 = 42; 这将允许更早y = r1地转换y = 42和执行,从而导致有问题的行为。在这种情况下,首先提交写入y

我的问题是,究竟是什么样的编译器优化?(我对编译器一无所知。)由于 42 只是有条件地编写,当if满足该语句时,编译器如何决定编写x

其次,即使编译器进行了这种推测性的优化,并且提交y = 42然后最终使r3 = 42,这是否违反了因果循环,因为现在已经没有因果关系了?

事实上,在同一文档(第 15 页,图 7)中有一个示例,其中提到了类似的因果循环是不可接受的。

那么这个执行顺序为什么在 JMM 中是合法的呢?

0 投票
2 回答
767 浏览

java - 同步和可见范围

我一直在阅读 Java 并发性,却忘记了使用相同锁的两个线程中的同步块也会影响变量的可见性,即使它们没有被定义为“易失性”。如果我有这样的代码

...并且 threadOne 和 threadTwo 将被不同的线程调用:

  1. 是否保证代码会跳出while循环?

  2. 如果我们从方程中删除变量 c 怎么办?我想知道是否只有 b 保证在 threadTwo 中可见,因为它在同步块内。

0 投票
1 回答
200 浏览

java - 是否有工具可以确定程序是否按照 JLS 中的定义“正确同步”?

Java 语言规范 7 (JLS7-17.4.5) 定义了一个“正确同步”的程序,如下所示:“当且仅当所有顺序一致的执行都没有数据竞争时,程序才能正确同步”。

JLS7-17.4.5 还指出:

如果没有正确的同步,可能会出现非常奇怪、令人困惑和违反直觉的行为。

因此,从程序员的角度来看,拥有一个工具来根据上述定义确定程序是否“正确同步”将非常有用。

有这样的工具吗?我用谷歌搜索找不到任何东西。如果没有这样的工具,是否有可能制作一个?

0 投票
0 回答
131 浏览

memory-barriers - JMM 食谱混淆的内存屏障示例

我对 JMM 食谱 http://g.oswego.edu/dl/jmm/cookbook.html中的障碍示例的编译器插入感到困惑

i = u (它不涉及来自 u 的不稳定负载和正常存储到 i 中吗?)

j = b (在我看来是来自 b 的正常加载和正常存储到 j )

根据cookbook中的查表,LoadLoad和LoadStore这两个屏障是哪里来的?

谢谢!

///////////////JSR 示例 ////

易变的诠释你;

整数 i,b,j;

我=你; //加载你

j = b; //加载b

0 投票
4 回答
1417 浏览

java - Java 并发 - 发布不可变对象(Java 并发实践)

在 Java Concurrency In Practice 中,作者指出

  1. 不可变对象可以通过任何机制发布
  2. 任何线程都可以安全地使用不可变对象,而无需额外的同步,即使不使用同步来发布它们也是如此。

这是否意味着以下成语可以安全地发布不可变对象?

会有数据竞赛吗?(这意味着线程 B 可能无法在线程 A 添加的列表中看到不可变对象)

非常感谢你。


此外,作者说如果 Resource 是不可变的,则以下代码是安全的。

第 16.3 节 初始化安全的保证允许正确构造的不可变对象在线程之间安全共享而无需同步,无论它们如何发布,即使使用数据竞争发布。(这意味着unsafeLazyInitialization如果它是不可变的,它实际上是安全Resource的。)

对于本题的第二部分,在另一个问题中详细讨论(点击这里

0 投票
10 回答
2710 浏览

java - 不变性和重新排序

下面的代码(Java Concurrency in Practice 清单 16.3)不是线程安全的,原因很明显:

然而,几页之后,在第 16.3 节中,他们指出:

UnsafeLazyInitialization如果 是不可变的,实际上是安全Resource的。

我不明白这种说法:

  • 如果Resource它是不可变的,那么观察该变量的任何线程resource都将看到它为 null 或完全构造(感谢 Java 内存模型提供的对 final 字段的强大保证)
  • 但是,没有什么可以阻止指令重新排序:特别是可以对 的两次读取resource进行重新排序(在 the 中读取if一次,在 中读取一次return)。resource因此,线程可以在条件中看到非空值,if但返回空引用 (*)。

我认为UnsafeLazyInitialization.getInstance()即使Resource是不可变的也可以返回 null 。是这样吗?为什么(或为什么不是)?


(*) 为了更好地理解我关于重新排序的观点,Jeremy Manson 的这篇博文解释了如何通过良性数据竞争安全地发布 String 的哈希码,以及如何删除由于可能的重新排序与我上面描述的非常相似,使用局部变量可能会导致哈希码错误地返回 0:

我在这里所做的是添加一个额外的读取:在返回之前第二次读取哈希。听起来很奇怪,也不太可能发生,第一次读取可以返回正确计算的哈希值,第二次读取可以返回 0!这在内存模型下是允许的,因为该模型允许对操作进行广泛的重新排序。第二次读取实际上可以在您的代码中移动,以便您的处理器在第一次之前执行它!

0 投票
4 回答
1274 浏览

java - 原始数组写入的 Java 并发可见性

我最近在我的代码库中发现了这个 gem:

这是这样使用的:

线程 1

线程 2

线程 1 是一个持续运行的后台更新线程。线程 2 是一个 HTTP 工作线程,它不关心它读取的内容是否一致或原子,只关心写入“最终”到达那里并且不会丢失作为并发神灵的祭品。

现在,这触发了我所有的警钟。自定义并发算法编写在不相关代码的深处。

不幸的是,修复代码并非易事。Java 对并发原始矩阵的支持不好。看起来解决此问题的最清晰方法是使用 a ReadWriteLock,但这可能会对性能产生负面影响。显然,正确性更重要,但似乎我应该在将其从性能敏感区域中剥离出来之前证明这是不正确的。

根据java.util.concurrent 文档,以下创建happens-before关系:

线程中的每个动作都发生在该线程中的每个动作之前,这些动作按程序的顺序出现在后面。

对 volatile 字段的写入发生在对同一字段的每次后续读取之前。volatile 字段的写入和读取具有与进入和退出监视器类似的内存一致性效果,但不需要互斥锁定。

所以听起来像:

  • 矩阵写入发生在发布之前()(规则 1)
  • publish() 发生在 syncChanges() 之前(规则 2)
  • syncChanges() 发生在矩阵读取之前(规则 1)

所以代码确实已经为矩阵建立了一个happens-before链。

但我不相信。并发很难,而且我不是领域专家。我错过了什么?这真的安全吗?