java - Java ConcurrentHashMap.computeIfPresent 值修改可见性
问题描述
假设我有一个以集合为值的并发映射:
Map<Integer, List<Integer> map = new ConcurrentHashMap<>();
map.putIfAbsent(8, new ArrayList<>());
我更新值如下:
map.computeIfPresent(8, (i, c) -> {
c.add(5);
return c;
});
我知道computeIfPresent
整个方法调用是原子执行的。但是,考虑到这个映射是由多个线程同时访问的,我有点担心对底层集合所做的修改的数据可见性。在这种情况下,调用后会在列表中看到值 5map.get
我的问题是,如果在方法调用map.get
中执行更改,则调用时将更改为在其他线程中可见的列表。computeIfPresent
请注意,如果我在执行更新操作之前参考列表,我知道对列表的更改将不可见。如果我map.get
在更新操作后引用列表(通过调用),我不确定对列表的更改是否可见。
我不确定如何解释文档,但在我看来,happens-before 关系将保证在这种特殊情况下对基础集合的更改的可见性
更正式地说,给定键的更新操作与报告更新值的键的任何(非空)检索具有发生前的关系
解决方案
该方法被记录为 的事实atomic
并不意味着太多visibility
(除非这是文档的一部分)。例如,为了使这更简单:
// some shared data
private List<Integer> list = new ArrayList<>();
public synchronized void addToList(List<Integer> x){
list.addAll(x);
}
public /* no synchronized */ List<Integer> getList(){
return list;
}
可以说addToList
确实是原子的,一次只能调用一个线程。但是一旦某个线程调用getList
- 根本无法保证visibility
(因为要建立它,它必须发生在同一个锁上)。所以可见性是在关注之前发生的事情,computeIfPresent
文档根本没有说明这一点。
相反,类文档说:
检索操作(包括 get)一般不会阻塞,因此可能与更新操作(包括 put 和 remove)重叠。
这里的关键点显然是重叠的,所以其他一些线程调用get
(从而获得了那个List
),可以List
在某种状态下看到它;不一定是computeIfPresent
开始的状态(在您实际调用之前get
)。请务必进一步阅读以了解某些实际含义。
现在到该文档中最棘手的部分:
检索反映了最近完成的更新操作在其开始时保持的结果。更正式地说,给定键的更新操作与报告更新值的该键的任何(非空)检索具有发生前的关系。
再次阅读关于完成的那句话,它说的是,当线程执行时,您唯一可以阅读的是 Listget
所处的最后一个完成状态。现在下一句说在两个动作之间建立之前发生了一个事件。
Think about it, a happens-before
is established between two subsequent actions (like in the synchronized example above); so internally when you update a Key
there could be a volatile written signaling that update has finished (I am pretty sure it's not done this way, just an example). For the happens before to actually work, get
has to read that volatile and see the state that was written to it; if it sees that state it means that happens before has been established; and I guess that by some other technique this is actually enforced.
So to answer your question, all the threads calling get
will see the last completed action
that happened on that key; in your case if you can guarantee that order, I'd say, yes, they will be visible.
推荐阅读
- python - 在函数中运行 try 和 except 并重新运行会导致 NoneType 返回。我该如何解决?
- azure - 无法在 Devops 步骤中设置 powershell 输出变量
- c# - 如何隐藏和显示 2 个不同的预制件?
- java - DTO 列表映射/流式传输
- python - 使用 PyGame 混音器播放声音无法正常工作
- c++ - 使用对象文件读取三角形数据网格
- python - 我无法在 PyGame 中使用我的平台游戏进行碰撞
- excel - 如何列出容器的所有元素?
- python - 返回文本文件中的行
- objective-c - 我可以在objective-c调用之前和之后设置路标吗?