面试题答案
一键面试线程安全机制差异
HashTable
:HashTable
是通过对几乎所有的操作(如put
、get
等)都使用synchronized
关键字进行同步,这意味着在同一时间只有一个线程能够访问HashTable
的方法,从而保证线程安全。这种方式是对整个HashTable
进行锁定,粒度较大,并发性能相对较差。
ConcurrentHashMap
:- 在 JDK 1.7 中,
ConcurrentHashMap
采用分段锁(Segment)的机制。它将整个哈希表分成多个段(Segment),每个段就像是一个小的HashTable
,每个段都有自己的锁。当一个线程访问某个段时,只会锁定这个段,而其他段仍然可以被其他线程访问,大大提高了并发性能。 - 在 JDK 1.8 中,
ConcurrentHashMap
摒弃了分段锁机制,采用了 CAS(Compare - and - Swap)和synchronized
相结合的方式。对于读操作,大部分时候不需要加锁,直接通过数组和链表(或红黑树)获取数据,只有在扩容等特殊情况下才需要加锁。对于写操作,使用synchronized
关键字锁住链表或红黑树的头节点,锁的粒度更细,进一步提升了并发性能。
- 在 JDK 1.7 中,
不同读写比例场景下的选择
- 读多写少场景:
ConcurrentHashMap
:由于读操作大部分无需加锁,性能优势明显。在高并发读多写少的场景下,ConcurrentHashMap
能充分发挥其并发优势,减少线程等待时间,提高系统整体性能。HashTable
:因为每次读操作都要获取整个HashTable
的锁,即使读操作之间不存在竞争,也会因为锁的存在而产生性能瓶颈,所以不适合读多写少场景。
- 写多读少场景:
ConcurrentHashMap
:在 JDK 1.8 中,写操作虽然采用了细粒度锁,但由于 CAS 操作和锁的竞争等因素,在写操作特别频繁时,可能会有一定的性能开销。不过相比HashTable
整个表锁定的方式,ConcurrentHashMap
仍然具有一定优势,因为其锁粒度更细,多个写操作可以并行在不同的链表或红黑树头节点上进行。HashTable
:虽然每次写操作都锁定整个表,但如果写操作频率不高,并且对代码简洁性有一定要求(HashTable
实现相对简单),也可以考虑使用HashTable
。但总体来说,在写多读少的高并发场景下,ConcurrentHashMap
通常是更好的选择。