面试题答案
一键面试内部数据结构设计
- 数组 + 链表/红黑树:ConcurrentHashMap在JDK1.8及以后采用数组 + 链表/红黑树的数据结构。数组中的每个元素称为桶(bucket),当链表长度超过一定阈值(默认为8)且数组容量达到64时,链表会转化为红黑树,以提高查找效率。这种结构设计可以将数据分散存储,减少哈希冲突,提升并发访问性能。
- 分段锁优化:在早期版本(JDK1.7及以前),ConcurrentHashMap采用分段锁机制,将数据分成多个段(Segment),每个段有独立的锁。不同段的操作可以并发执行,降低锁竞争。虽然JDK1.8中不再使用Segment,但这种分段思想在一定程度上依然存在,通过CAS操作和轻量级锁等方式来减少锁粒度。
同步机制
- CAS操作:在更新元素时,ConcurrentHashMap大量使用CAS(Compare - and - Swap)操作。CAS操作是一种乐观锁机制,它尝试将内存中的值与预期值进行比较,如果相等则将其更新为新值,否则不进行任何操作。例如在插入新元素时,会使用CAS尝试更新数组中的桶。这种操作不需要获取锁,在大多数情况下可以快速完成,提升了并发性能。
- Synchronized锁:当需要对桶进行更复杂的操作,如扩容、链表转红黑树等,会使用synchronized关键字对桶进行加锁。但由于锁的粒度是桶级别,而不是整个Map,所以其他桶的操作依然可以并发执行,减少了锁的竞争范围,保证了高并发性能。
与Java内存模型特性的配合
- 可见性:Java内存模型规定,对volatile变量的写操作会立即刷新到主内存,读操作会从主内存重新读取。ConcurrentHashMap中的一些关键变量(如sizeCtl)被声明为volatile。当一个线程修改了这些变量,其他线程能够立即看到修改后的结果,保证了数据的可见性,从而确保在高并发环境下数据的一致性。
- 有序性:Java内存模型通过happens - before规则来保证操作的有序性。在ConcurrentHashMap中,由于CAS操作和synchronized锁的使用,它们之间存在happens - before关系。例如,一个线程在释放synchronized锁之前对共享变量的修改,在其他线程获取该锁后能够看到这些修改,这就保证了操作的有序性,避免了数据竞争和不一致问题。
- 原子性:对于基本数据类型的读写操作,在没有使用volatile关键字时,可能不是原子性的。但ConcurrentHashMap通过CAS操作和锁机制保证了关键数据更新操作的原子性。例如,在使用CAS更新桶中的元素时,这个操作是原子的,要么成功,要么失败,不会出现部分更新的情况,确保了数据的一致性和线程安全。