✅ConcurrentHashMap是如何保证fail-safe的?

典型回答

在 JDK 1.8 中,ConcurrentHashMap作为一个并发容器,他是解决了fail-fast的问题的,也就是说,他是一个fail-safe的容器。 通过以下两种机制来实现 fail-safe 特性:

首先,在 ConcurrentHashMap 中,遍历操作返回的是弱一致性迭代器,这种迭代器的特点是,可以获取到在迭代器创建前被添加到 ConcurrentHashMap 中的元素,但不保证一定能获取到在迭代器创建后被添加/删除的元素。

弱一致性是指在并发操作中,不同线程之间的操作可能不会立即同步,但系统会在某个时刻趋于一致。这种弱一致性的特性有助于实现 fail-safe 行为,即使在迭代或操作过程中发生了并发修改,也不会导致异常或数据损坏。即他不会抛出ConcurrentModifiedException

另外。在 JDK 1.8 中,ConcurrentHashMap 中的 Segment 被移除了,取而代之的是使用类似于cas+synchronized的机制来实现并发访问。在遍历 ConcurrentHashMap 时,只需要获取每个桶的头结点即可,因为每个桶的头结点是原子更新的,不会被其他线程修改。这个设计允许多个线程同时修改不同的桶,这减少了并发修改的概率,从而降低了冲突和数据不一致的可能性。

也就是说,ConcurrentHashMap 通过弱一致性迭代器和 Segment 分离机制来实现 fail-safe 特性,可以保证在遍历时不会受到其他线程修改的影响。

扩展知识

弱一致性保障

ConcurrentHashMap 提供的是弱一致性保障,这是因为在多线程并发修改 ConcurrentHashMap 时,可能会出现一些短暂的不一致状态,即一个线程进行了修改操作,但是另一个线程还没有看到这个修改。因此,在并发修改 ConcurrentHashMap 时,不能保证在所有时刻 ConcurrentHashMap 的状态都是一致的。

首先就是因为前面提到的弱一致性迭代器,在 ConcurrentHashMap 中,使用迭代器遍历时,不能保证在迭代器创建后所有的元素都被迭代器遍历到。这是因为在迭代器遍历过程中,其他线程可能会对 ConcurrentHashMap 进行修改,导致迭代器遍历的元素发生变化。为了解决这个问题,ConcurrentHashMap 提供了一种弱一致性迭代器,可以获取到在迭代器创建前被添加到 ConcurrentHashMap 中的元素,但是可能会无法获取到迭代器创建后被添加/删除的元素。

原文: https://www.yuque.com/hollis666/xkm7k3/ghmzuzasiaic6xga