3 回答
TA贡献1776条经验 获得超12个赞
澄清你的问题:
你提供一些对外担保这种Map.computeIfPresent()
被称为前 Map.get()
。
您还没有说明您是如何做到这一点的,但假设您是通过使用JVM 提供的具有发生前语义的东西来做到这一点的。如果是这种情况,那么,仅仅保证List.add()
是可见的线程调用Map.get()
只需协会的之前发生关系。
现在回答您实际提出的问题:正如您所指出的,更新操作与随后调用 access 方法之间存在发生在之前的关系。很自然地,在和 的结尾之间有一个happens-before关系。ConcurrentHashMap.computeIfPresent()
ConcurrentMap.get()
List.add()
ConcurrentHashMap.computeIfPresent()
综合起来,答案是肯定的。
有一个保证其他线程将看到5
在List
通过获取Map.get()
,提供你担保Map.get()
实际上是后调用 computeIfPresent()
端(如问题所述)。如果后一个保证出错并且Map.get()
在computeIfPresent()
结束前以某种方式被调用,则无法保证其他线程将看到什么,因为ArrayList
它不是线程安全的。
TA贡献1848条经验 获得超6个赞
事实上,该方法被记录为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文档根本没有对此进行任何说明。
相反,类文档说:
检索操作(包括获取)一般不会阻塞,因此可能与更新操作(包括放置和删除)重叠。
这里的关键点显然是overlap,所以其他一些线程调用get(从而获得了那个List),可以List在某种状态下看到它;不一定是computeIfPresent开始的状态(在您实际调用之前get)。请务必进一步阅读以了解某些实际可能意味着什么。
现在到该文档中最棘手的部分:
检索反映了最近完成的更新操作的结果。更正式地,给定键的更新操作与报告更新值的该键的任何(非空)检索具有发生前关系。
再读一遍关于完成的句子,它说的是当一个线程执行时你唯一能读到的get是List所处的最后一个完成状态。现在下一句话说在两个动作之间建立之前发生了。
想一想,ahappens-before是在两个后续动作之间建立的(就像上面的synchronized例子);因此,当您在内部更新 a 时Key,可能会有一个不稳定的书面信号表明更新已完成(我很确定它不是以这种方式完成的,只是一个例子)。对于在实际工作之前发生的事情,get必须读取 volatile 并查看写入它的状态;如果它看到那个状态,则意味着之前发生的事情已经建立;我猜想通过其他一些技术,这实际上是强制执行的。
所以要回答你的问题,所有调用的线程get都会看到last completed action那个键上发生的事情;在您的情况下,如果您可以保证该订单,我会说,是的,它们将是可见的。
添加回答
举报