深入解析线程安全集合:ConcurrentHashMap与阻塞队列在多线程环境下的应用

一、线程安全的集合

深入解析线程安全集合:ConcurrentHashMap与阻塞队列在多线程环境下的应用

如果多线程要并发地对一个数据结构进行修改,比如散列表。那么就很有可能会把这个数据结构破坏掉。例如,有一个线程可能需要向表中插入一个新的元素。当在调整散列表各个桶之间的链接关系时,如果它被剥夺了控制权。而此时如果另一个线程也开始遍历同一个链表,就可能会遇到无效的链接并导致混乱,有可能会抛出异常或者陷入死循环。

可以通过提供锁的方式来对共享数据结构进行保护。然而,选择线程安全的实现当作替代方案或许会更加容易一些。阻塞队列属于线程安全的集合。

1>高效的映射表、集合和队列

java.util.concurrent 包提供了多种高效的数据结构实现。其中包括映射表的 ConcurrentHashMap,有序表的 ConcurrentSkipListMap,集合的 ConcurrentSkipListSet 以及队列的 ConcurrentLinkedQueue。

这些集合运用了复杂的算法。它能够允许同时访问数据结构的不同部分。这样做的目的是使竞争达到极小化。

大多数集合的 size 方法能在常量时间内操作,然而与它们不同的是,这里的 size 方法不必再常量时间内操作。要确定这样的集合当前的大小,通常需要进行遍历。

集合会返回弱一致性的迭代器。这表明迭代器未必能体现出构造之后的所有修改。然而,它们不会两次返回同一值,也不会抛出 ConcurrentModificationException 异常。

与之形成对照的是,集合在迭代器构造之后若发生改变,java.util 包中的迭代器就会抛出 ConcurrentModificationException 异常。

并发的散列映射表能够高效地支持众多的读者以及一定数量的写者。在默认情形下,假定能够有最多 16 个写者线程同时进行操作。可以存在比 16 个更多的写者线程,不过,倘若在同一时间超过 16 个,其他线程就会暂时被阻塞。能够指定更大数目的构造器,然而,可能没有这样的必要。

2>写数组的拷贝

CopyOnWriteArrayList 是线程安全的集合,所有的修改线程会对底层数组进行复制。CopyOnWriteArraySet 也是线程安全的集合,同样所有的修改线程会对底层数组进行复制。如果在集合上进行迭代的线程数超过了修改线程数,这种安排是很有用的。当构建一个迭代器时,它会包含一个对当前数组的引用。如果数组后来被修改了,那么迭代器依然会引用旧数组。然而,集合的数组已经被替换掉了。所以,旧的迭代器有着一致的试图,并且在访问它时不需要任何同步开销。

3>较早的线程安全集合

Java从初始版本起,Vector 类和 Hashtable 类就给出了线程安全的动态数组与散列表的实现。如今这些类已被弃用,ArrayList 类和 HashMap 类取而代之。这些类并非线程安全的,而集合库中提供了别样的机制。任何集合类借助同步包装器可变为线程安全的:

ListsynchArrayList 是通过 Collections 的 synchronizedList 方法对新创建的 ArrayList 进行同步处理后得到的。新创建了一个 ArrayList,然后用 Collections 的 synchronizedList 方法将其同步,得到的结果就是 synchArrayList。());
MapsynchHashMap 是由 Collections 类的 synchronizedMap 方法对一个新创建的 HashMap 进行同步处理后得到的。());

结果集合的方法使用锁加以保护,提供了线程的安全访问。

要确保没有线程通过原始的非同步方法去访问数据结构。一种很便利的做法是,保证不保存任何指向原始对象的引用,只需构造一个集合,然后马上将其传递给包装器。

如果在另一个线程有可能进行修改的情况下要对集合进行遍历,那就依然需要运用“客户端”锁定。

synchronized (synchHashMap) {
			Iteratoriter 是同步哈希映射的键集的迭代器。
		}

使用 foreach 循环时需要使用同样的代码,原因是循环使用了迭代器。

在迭代过程中,如果别的线程修改了集合,那么迭代器就会失效,并且会抛出异常。所以仍然需要同步,这样并发的修改就能够被可靠地检查出来。最好使用 java.util.concurrent 包中定义的集合,而不要使用同步包装器中的集合。特别是,若他们访问的是不同的桶,因为 ConcurrentHashMap 已被精心实现,多线程能够访问这些桶且不会相互阻塞。有一个特殊情况是经常被修改的数组列表。在这种情形下,同步的 ArrayList 可能比 CopyOnWriteArrayList 更优。

比较 HashTable 与 ConcurrentHashMap 。

Hashtable 和 ConcurrentHashMap 有相同点,它们都是线程安全的,都能够在多线程环境中运行。并且它们的 key 和 value 都不能为 null。

区别:两者主要在性能方面存在差异。Hashtable 的所有操作会锁住整个对象,这样虽然能保证线程安全,但其性能较差;ConcurrentHashMap 内部使用 Segment 数组,每个 Segment 类似 Hashtable,在“写”线程或部分特殊“读”线程中锁住的是某个 Segment 对象,而其他线程能够并发执行其他的 Segment 对象。

你可能想看:
« 上一篇
下一篇 »

相关推荐

java中什么是冒泡,冒泡算法

2026年09月06日

27阅读

java中冒号,java双冒号

2026年09月06日

26阅读

java中的->,java中的是什么意思

2026年09月06日

24阅读

java中的super用法,Java中super的说明

2026年09月06日

28阅读

java中反射的应用,java中反射的应用场景

2026年09月06日

26阅读

java中开关的定义,Java关键字有哪些

2026年09月06日

26阅读