口味王网站建设的设计方案,seo推广优化官网,wordpress网址转换,绍兴网站开发公司在 JDK 1.8 的 ConcurrentHashMap 中#xff0c;之所以对“容器为空”和“计算位置为空”采取不同的处理方式#xff0c;主要是因为 并发场景下的性能优化和并发安全保证。我们可以分开来看这两种情况#xff1a;
1. 容器为空时#xff0c;使用 volatile CAS 初始化 原因…在 JDK 1.8 的 ConcurrentHashMap 中之所以对“容器为空”和“计算位置为空”采取不同的处理方式主要是因为 并发场景下的性能优化和并发安全保证。我们可以分开来看这两种情况
1. 容器为空时使用 volatile CAS 初始化 原因ConcurrentHashMap 采用 懒加载并不会在构造时就初始化所有桶NodeK, V[] table。 实现当第一次插入元素时会先判断 table 是否为空
if (tab null || (n tab.length) 0) tab initTable();initTable() 方法使用 CASCompare-And-Swap 操作来保证线程安全的初始化。 为什么用 CAS 而不是 synchronized? 目的是减少不必要的锁竞争提高并发性能。由于初始化操作通常只需要执行一次典型的 双重检查锁 模式CAS 在多数情况下不会失败所以开销较小。
2. 计算出的位置为空时使用 CAS 插入
原因如果某个桶即 table[index]位置为空说明没有哈希冲突我们可以直接尝试插入数据。实现使用 CAS 方式直接插入 if (casTabAt(tab, i, null, new NodeK, V(hash, key, value, null))) {break; // 插入成功退出循环
}为什么用 CAS 而不是 synchronized? 因为这个位置是 null没有竞争所以可以直接尝试用 无锁的 CAS 操作 插入避免加锁的开销提高性能。
3. 计算出的位置不为空时使用 synchronized 原因如果 table[index] 位置已经有元素了可能会遇到 哈希冲突需要遍历该链表或红黑树进行替换或追加。 实现 先通过 synchronized 锁住该桶synchronized (f)。然后遍历这个桶 如果 key 已存在则更新 value。如果 key 不存在则添加新的节点链表 or 红黑树。 插入完成后判断链表长度是否达到阈值8如果达到就转换为红黑树。 为什么用 synchronized 而不是 CAS? CAS 只能保证单个变量的原子性而不能保证整个链表或树结构的原子性。当多个线程同时修改一个桶时直接用 synchronized 保护整个桶的操作避免复杂的 CAS 失败重试提高效率。
JDK 1.8 在 ConcurrentHashMap 中通过 分阶段使用 CAS 和 synchronized既保证了 高并发性能又保证了 线程安全这就是它不同情况下采用不同方式的原因。