Java ConcurrentHashMap.computeIfPresent值修改可见性

Mar*_*šić 14 java multithreading concurrenthashmap memory-visibility

假设我有一个带有集合的并发映射值:

Map<Integer, List<Integer> map = new ConcurrentHashMap<>();
map.putIfAbsent(8, new ArrayList<>());
Run Code Online (Sandbox Code Playgroud)

我更新了如下值:

map.computeIfPresent(8, (i, c) -> {
    c.add(5);
    return c;
});
Run Code Online (Sandbox Code Playgroud)

我知道computeIfPresent整个方法调用是以原子方式执行的.但是,考虑到这个映射是由多个线程同时访问的,我有点担心对底层集合所做的修改的数据可见性.在这种情况下,调用后将在列表中看到值5map.get

我的问题是map.get如果在computeIfPresent方法调用中执行了更改,则在调用时将更改为列表在其他线程中可见.

请注意,如果我在执行更新操作之前参考列表,我知道列表的更改将不可见.如果我map.get在更新操作后引用列表(通过调用),我不确定列表的更改是否可见.

我不确定如何解释文档,但在我看来发生这种情况 - 在关系之前将保证在这种特定情况下对底层集合的更改的可见性

更正式地说,给定密钥的更新操作承担与该密钥的任何(非空)检索之前发生的关系,报告更新的值

ant*_*tak 1

为了澄清你的问题:

您正在提供一些外部保证,例如before 调用的Map.computeIfPresent()保证。 Map.get()

您还没有说明如何执行此操作,但假设您正在使用JVM 提供的“先发生”语义来执行此操作。如果是这种情况,那么仅通过关联发生之前List.add()关系就可以保证线程调用可见。Map.get()

现在回答您实际提出的问题:正如您所注意到的,更新操作和访问方法的后续调用之间存在“发生之前”关系。自然地, 和的结尾之间存在发生在之前的关系。ConcurrentHashMap.computeIfPresent()ConcurrentMap.get()List.add()ConcurrentHashMap.computeIfPresent()

综合起来,答案是肯定的

如果您保证在结束实际调用(如问题中所述),则可以保证其他线程将5List获取的 through中看到。如果后一个保证出现问题并且在结束之前以某种方式被调用,则无法保证其他线程会看到什么,因为它不是线程安全的。Map.get()Map.get() computeIfPresent()Map.get()computeIfPresent()ArrayList