并发集合类(如ConcurrentHashMap)的线程安全实现
- 分段锁(Segment Locking)或锁分离(Lock - Striping)
- 以
ConcurrentHashMap
为例,在Java 7及之前版本中,它采用了分段锁机制。ConcurrentHashMap
内部被划分为多个段(Segment),每个段类似于一个小的哈希表,拥有自己的锁。这样,不同的线程可以同时访问不同的段,从而提高了并发性能。 - 在Java 8及之后版本中,
ConcurrentHashMap
采用了锁分离技术结合CAS(Compare - And - Swap)操作。它使用Node
数组来存储数据,每个Node
可以看作是一个链表节点或者红黑树节点(当链表长度超过一定阈值时会转换为红黑树)。对于更新操作,它首先尝试通过CAS操作来修改节点的值或者结构,如果CAS失败,则会获取相应的锁(如synchronized
锁或者ReentrantLock
锁)来进行操作。这种机制既保证了线程安全,又提高了并发性能。
- 以
- 原子操作
- 并发集合类利用了Java中的原子类(如
AtomicInteger
等)来实现一些操作的原子性。例如,在ConcurrentHashMap
中,对于计数的操作可以通过原子类来实现,避免了使用锁带来的性能开销。
- 并发集合类利用了Java中的原子类(如
- 可见性保证
- 并发集合类通过
volatile
关键字或者内存屏障等技术来保证变量的可见性。例如,在ConcurrentHashMap
中,对于共享变量的修改能够及时地被其他线程看到。
- 并发集合类通过
并发集合类与传统同步集合类(如Hashtable)的性能对比
- 并发性能
Hashtable
是传统的同步集合类,它在所有方法上都使用了synchronized
关键字,这意味着当一个线程访问Hashtable
的方法时,其他线程必须等待。这种粗粒度的锁机制在多线程环境下会导致严重的性能瓶颈。- 相比之下,并发集合类(如
ConcurrentHashMap
)通过更细粒度的锁(如分段锁或者锁分离结合CAS操作),允许多个线程同时访问不同的部分,从而大大提高了并发性能。例如,在高并发场景下,ConcurrentHashMap
的读操作几乎不需要加锁,而Hashtable
的读操作也需要获取锁。
- 扩容机制
Hashtable
在扩容时需要锁定整个表,这会导致在扩容期间其他线程无法进行读写操作。ConcurrentHashMap
在扩容时采用了更灵活的机制,它可以分阶段进行扩容,允许读操作在扩容期间继续进行,写操作也可以在一定程度上并发进行,从而提高了扩容时的性能。
并发集合类与传统同步集合类(如Hashtable)的功能对比
- 迭代器
Hashtable
的迭代器是快速失败的(fail - fast)迭代器。当在迭代过程中有其他线程修改了Hashtable
(除了通过迭代器自身的remove
方法),迭代器会抛出ConcurrentModificationException
异常。ConcurrentHashMap
的迭代器也是快速失败的,但在多线程环境下,它的行为更加安全。由于ConcurrentHashMap
的并发控制机制,在迭代过程中可以进行一些并发修改操作而不会抛出异常(当然,如果修改不符合并发规则,仍然会抛出异常)。
- 功能丰富度
Hashtable
是一个比较基础的同步集合类,功能相对单一。ConcurrentHashMap
提供了更多高级功能,如原子性的复合操作(putIfAbsent
、remove
、replace
等),这些操作可以在多线程环境下更加方便和安全地进行。
总结
并发集合类(如ConcurrentHashMap
)通过多种技术实现了高效的线程安全,与传统的同步集合类(如Hashtable
)相比,在性能和功能上都有很大的优势。在多线程环境下,推荐使用并发集合类来提高程序的性能和可靠性。