面试题答案
一键面试ConcurrentHashMap实现线程安全的方式
- JDK 1.7:
- 分段锁机制:将数据分成多个Segment,每个Segment类似于一个小的Hashtable,独立进行加锁。当一个线程访问某个Segment时,其他线程可以访问其他Segment,从而提高并发度。例如,假设有16个Segment,理论上最多可以同时支持16个线程并发访问不同的Segment。
- ReentrantLock:每个Segment内部使用ReentrantLock来保证线程安全。在进行put、get等操作时,首先定位到对应的Segment,然后获取该Segment的锁。
- JDK 1.8:
- Node数组 + CAS + synchronized:摒弃了分段锁机制。当进行put操作时,首先通过哈希值定位到数组的某个位置。如果该位置为空,使用CAS操作直接插入;如果不为空,且该位置的头节点的hash值为MOVED(正在扩容),则协助扩容;否则,使用synchronized对该头节点加锁进行插入操作。
- 红黑树优化:当链表长度超过一定阈值(默认为8)且数组长度达到64时,链表会转化为红黑树,提高查找效率,进一步保证并发场景下的性能。
与Hashtable相比在性能方面的优势
- 并发度高:Hashtable使用一个全局锁,在高并发情况下,所有线程都需要竞争这一把锁,导致性能瓶颈。而ConcurrentHashMap无论是JDK 1.7的分段锁还是JDK 1.8的优化机制,都允许更多线程同时访问不同部分的数据,大大提高了并发度。
- 读操作性能好:在ConcurrentHashMap中,读操作通常不需要加锁(除非读到正在扩容的节点)。而Hashtable读操作也需要获取锁,这使得ConcurrentHashMap在高并发读场景下性能更优。
JDK 1.7和JDK 1.8在高并发读写场景下性能差异及原因
- 性能差异:在高并发读写场景下,JDK 1.8的ConcurrentHashMap性能通常优于JDK 1.7。
- 原因:
- 锁粒度:JDK 1.7的分段锁虽然提高了并发度,但锁粒度相对还是比较大,在某些情况下,一个Segment可能会被多个线程频繁竞争。而JDK 1.8使用synchronized针对单个节点加锁,锁粒度更小,减少了锁竞争的概率。
- 数据结构优化:JDK 1.8引入了红黑树,当链表长度过长时,查找性能从O(n)提升到O(log n),在高并发读写场景下,数据量较大时,查找效率的提升对整体性能有较大帮助。
- CAS操作:JDK 1.8在插入操作时优先使用CAS,只有在CAS失败时才使用synchronized,减少了锁竞争带来的开销,提高了并发性能。