该方法被记录为 的事实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.