java cocurrent包提供了很多并发容器,在提供并发控制的前提下,通过优化,提升性能。本文主要讨论常见的并发容器的实现机制和绝妙之处,但并不会对所有实现细节面面俱到。
java collection framework提供了丰富的容器,有map、list、set、queue、deque。但是其存在一个不足:多数容器类都是非线程安全的,即使部分容器是线程安全的,由于使用sychronized进行锁控制,导致读/写均需进行锁操作,性能很低。
java collection framework可以通过以下两种方式实现容器对象读写的并发控制,但是都是基于sychronized锁控制机制,性能低:
1. 使用sychronized方法进行并发控制,如HashTable 和 Vector。以下代码为Vector.add(e)的java8实现代码:
2. 使用工具类Collections将非线程安全容器包装成线程安全容器。以下代码是Collections.synchronizedMap(Map<K,V> m)将原始Map包装为线程安全的SynchronizedMap,但是实际上最终操作时,仍然是在被包装的原始m上进行,只是SynchronizedMap的所有方法都加上了synchronized锁控制。
为了提供高效地并发容器,java 5在java.util.cocurrent包中 引入了并发容器。
在jdk1.7中,ConcurrentHashMap是这样处理:
ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成。Segment实际是一种可重入锁(ReentrantLock),也就是用于分段的锁。HashEntry则用于存储键值对数据。一个ConcurrentHashMap里包含一个Segment数组。Segment的结构和HashMap类似,是一种数组和链表结构。一个Segment里包含一个HashEntry数组,每个HashEntry是一个链表结构的元素,每个Segment守护着一个HashEntry数组里的元素,当对HashEntry数组的数据进行修改时,必须首先获得与它对应的Segment锁。
ConcurrentHashMap在1.8下的实现
采用transient volatile Node<K,V>[] table保存数据,采用table数组元素作为锁,从而实现了对每一行数据进行加锁,进一步减少并发冲突的概率, 理想情况下talbe数组元素的大小就是其支持并发的最大个数。对于个数超过8且table数组长度超过64(默认值)的链表,jdk1.8中采用了红黑树的结构,那么查询的时间复杂度可以降低到O(logN),可以改进性能。
ConcurrentHashMap数据结构和HashMap相同
下面是putVal方法,在方法中,我们可以看到,为了最大限度的提高并发能力,sychronized锁住的是哈希表中的头节点元素,理想情况下talbe数组元素的大小就是其支持并发的最大个数;并且使用cas尝试插入table中为null的位置;
扩容机制
ConcurrenthashMap在无参构造情况,table长度为0 ,在进行第一次put操作时,会进行初始化table操作;
ConcurrenthashMap并发效率虽然很高,但不足之处在于并发扩容的时候,由于操作的table都是同一个,不像1.7中分段控制,所以这里需要等扩容完之后,所有的读写操作才能进行,这样扩容就成为了整个并发的一个瓶颈点。
所以在JDK8的源码里面就引入了一个ForwardingNode类,在一个线程发起扩容的时候,就会改变sizeCtl这个值,其含义如下:
sizeCtl :默认为0,用来控制table的初始化和扩容操作,具体应用在后续会体现出来。
-1 代表table正在初始化
-N 表示有N-1个线程正在进行扩容操作
其余情况:
1、如果table未初始化,表示table需要初始化的大小。
2、如果table初始化完成,表示table的容量,默认是table大小的0.75倍
未完待续
补充: put 和 putIfAbsent区别
上面有个变量onlyIfAbsent, 默认值是false,要知道他的作用,需要先了解put和putifAbsent方法的区别;
put方法,官方解释“the previous value associated with key, or null if there was no mapping for key. A null return can also indicate that the mappreviously associated null with key.”即,更新旧值,同时返回这个旧值;
putIfAbsent方法, 官方解释 If the specified key is not already associatedwith a value, associate it with the given value. ”即,如果key不存在,更新并返回null,否则,不更新,返回旧值;
留言与评论(共有 0 条评论) |