gpt4 book ai didi

java - 安全发布和ConcurrentHashMap

转载 作者:行者123 更新时间:2023-12-02 00:59:21 28 4
gpt4 key购买 nike

假设我们有一个 Container 类

class Container {
private final Map<LongHolder, Integer> map = new ConcurrentHashMap<>();
static class LongHolder {
private final Long i;
private LongHolder(Long i) {
this.i = i;
}
}

public LongHolder createValue(Long i) {
LongHolder v = new LongHolder(i);
map.put(v, 1)
return LongHolder;
}

public void doSmth(LongHolder longHolder) {
map.get(longHolder);
... // do smth
}
}

是否可以通过以下方式重新排序操作:createValue 对 LongHolder 的引用会转义该方法,从而在将其放入映射之前可用于其他线程?在这种情况下,我们可以获取一个 LongHolder 引用表单 createValue 并将其传递给另一个线程中的 doSmth,该线程在 map 中看不到它。是否可以?如果没有请解释一下。

更新:ConcurrentHashMap 的 Javadoc 指出检索反射(reflect)了最近完成的更新操作的结果 发病。 (更正式地说,给定 key 的更新操作具有 与任何(非空)检索发生之前的关系 该键报告更新后的值。) 但有最近完成的更新操作 问题正是关于重新排序时的这种微妙情况 可能会以实际更新操作的方式发生 不完整,引用在 put 之前转义。Javadoc ConcurrentHashMap 仅说明给定键具有一个事实 与该键的任何(非空)检索发生之前关系 换句话说,只有当我们检索到该 key 时,我们才能进行辩论 关于发生在与此 key 的更新操作的关系之前。

我只能提出一些其他原因来解释为什么它不会发生:

1)重新排序规则比我想象的更严格,我们不能假设仲裁重新排序(或者我们可以吗?规则是什么?)。

2) put 有一些神奇的属性,不允许重新排序,并且 return 仅在完成 ​​put 后才会发生。在这种情况下,这些属性是什么?

最佳答案

假设线程 AB引用Container实例。

(由于 Container.map 被声明为 final ,其值将被安全发布。此外,由于它引用 ConcurrentHashMap ,因此不需要同步来维护线程安全。)

现在假设线程 A来电 Longholder holder = container.createValue(x)并以某种方式通过holder到线程 B 。你的问题是,如果 B来电 doSmth通过该持有者,将map.get(holder)打电话在 map 上看到它了吗?

答案是肯定的,会的。

ConcurrentHashMap的规范说的是:

"Retrievals reflect the results of the most recently completed update operations holding upon their onset. (More formally, an update operation for a given key bears a happens-before relation with any (non-null) retrieval for that key reporting the updated value.)".

这意味着 put发生过调用该线程 A制作及后续get调用线程B 。这反过来意味着:

  • get 将会找到LongHolder键,并且
  • get 返回正确的值。

LongHolder 的值对象的i字段也将如预期,并且即使 LongHolder是一个可变持有者。

(请注意,在这里声明不可变的 LongHolder 没有多大意义。它相当于 java.lang.Long ...尽管具有更简单的 API。)

<小时/>

Can operations be reordered in a such manner that the reference to LongHolder from createValue escapes the method and thus becomes available for other threads before putting it into the map?

基本上,没有。相关操作是这样的:

      LongHolder v = new LongHolder(i);
map.put(v, 1)
return v; // corrected typo
  1. 没有对源代码进行任何有意义的重新排序。
  2. 如果您正在讨论编译器对正在运行的线程进行重新排序 createValue ,JLS 不允许。禁止任何改变线程内可见性的重新排序。
  3. v的发布由于 get 的属性,通过 map 是安全的出版物。和put 。 (在实现过程中,存在内存屏障,禁止对 getput 调用进行有害的重新排序。)

  4. 和 3. 是发生在关系的结果

现在,如果代码更改为:

      LongHolder v = new LongHolder(i);
map.put(v, 1)
// Somehow modify v.i
return v;

那么将是一种不安全的发布形式。自从更改为v.i通过A发生在 put 之后,发生在 put 之间的关系之前和 getB不足以保证 v.i 的新值在线程中可见 B 。那是因为链接不再起作用。

现在,我想如果 createValue 的结果调用线程A以不安全的方式传递给另一个线程( BC ),则不能保证后者看到 v.i 的正确值...如果LongHolder是可变的。但这对于createValue来说不是问题。/doSmth代码。并公布值v通过 map 是安全的。

但我认为关于重新排序的讨论没有捕获要点。禁止任何违反内存模型保证的可见性语义的重新排序。 JIT 编译器不允许这样做。因此,您只需进行发生在之前的分析。

关于java - 安全发布和ConcurrentHashMap,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60914276/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com