Java多线程-并发容器集合

并发容器集合

java.util包下提供了一些容器类,其中vectorHashTable是线程安全的容器类。但这些容器类实现同步的方式是通过对方法加锁(synchronized)来实现的。这样的话读写操作均需要锁操作,造成效率低下。

因此,Java5之后提供了一些并发容器来在多线程下代替同步容器,提高容器的并发访问性,同时定义了线程安全的复合操作。

并发容器类

并发Map

ConcurrentMap接口

ConcurrentMap接口继承了Map接口,并在Map接口上新定义了几个方法:

1
2
3
4
5
6
7
8
9
10
public interface ConcurrentMap<K, V> extends Map<K, V> {
//插入元素
V putIfAbsent(K var1, V var2);
//移除元素
boolean remove(Object var1, Object var2);
//替换元素
boolean replace(K var1, V var2, V var3);
//替换元素
V replace(K var1, V var2);
}

putIfAbsent:如果插入的key相同,则不替换原有的value值。

remove:这个remove方法增加了对value的判断,如果要删除的key-value不能与Map中的key-value对应上,则不会删除该元素。

replace(K var1, V var2, V var3):增加了对value的判断,如果key-oldValue能与Map中原有的key-value对应上,才进行替换操作。

replace(K var1, V var2):不会对Map中原有的key-value进行比较,如果存在key,则直接替换。

ConcurrentHashMap类

参考链接

ConcurrentHashMap类提供了一种粒度更细的加锁机制来实现多线程下的更高性能,这种机制叫做分段锁,分段锁在并发环境下会实现更高的吞吐量,而在单线程环境下只会损失非常小的性能。

ConcurrentHashMap会把数据分成一段段的来存储,然后给每一段数据分配一把锁,当一个线程占用锁访问一个段数据时,其他线程能够访问其他数据段。执行有些需要跨段的方法时,如size()containsValue()等,可能需要锁定整个表而不仅仅是某个段,这需要按照顺序锁定所有段,操作完毕后,按顺序释放所有段的锁。

ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成。Segment是一种可重入锁ReentrantLock,在ConcurrentHashMap中扮演锁的角色,HashEntry则用于存储键值对数据。一个ConcurrentHashMap里包含一个Segment数组,Segment的结构和HashMap类似,是一种数组和链表结构。一个Segment中包含一个HashEntry数组,每个HashEntry是一个链表结构的元素,每个Segment守护着一个HashEntry数组中的元素,当对HashEntry中的元素进行修改时,必须首先获得它的Segment锁。

具体的ConcurrentHashMap源码分析会另行学习。

ConcurrentNavigableMap接口与ConcurrentSkipListMap类

ConcurrentNavigableMap接口继承了NavigableMap接口,这个接口提供了针对给定搜索目标返回最接近匹配项的导航方法。

ConcurrentNavigableMap接口的主要实现类是ConcurrentSkipListMap类。这个类使用的底层数据结构是跳表(SkipList)的数据结构。

跳表是一种以空间换时间的数据结构,可以使用CAS操作保证并发安全性。详情参考下面链接:

什么是跳表

跳表(SkipList)及ConcurrentSkipListMap源码解析

并发Queue

JDK并没有提供线程安全的List类,因为对于List来说,实现一个通用并且没有并发瓶颈的线程安全List很难。

但JDK提供了队列和双端队列的线程安全类:ConcurrentLinkedDequeConcurrentLinkedQueue。这两个类是通过CAS来实现线程安全的。

并发Set

JDK提供了ConcurrentSkipListSet,是线程安全的有序集合。底层使用ConcurrentSkipMap实现。

-------------本文结束感谢您的阅读-------------