1.ConcurrentHashMap的实现原理 1.7 JDK7中的ConcurrentHashMap由Segment和HashEntry组成,即ConcurrentHashMap把哈希桶数组切分成小数组(Segment) , 每个小数组有n个HashEntry组成。将数据分为一段一段的存储,然后给每段数据配一 把锁,当一个线程占用锁访问其中一段数据时,其他段的数据 也能被其他线程访问,实现并发访问。
1.8 JDK8中的ConcurrentHashMap选择了与HashMap相同的Node数组+链表+红黑树结构在锁的实现上,抛弃了原有的Segment分段锁,采用CAS + synchronized实现更加细粒度的锁。将锁的级别控制在了更细粒度的哈希桶数组元素级别,只需要锁住这个链表头节点(红黑树的根节点),就不会影响其他的哈希桶数组元素的读写,大大提高了并发度。
2.ConcurrentHashMap的get方法是否要加锁 get方法不需要加锁。因为Node和HashEntry的元素value和指针next是用volatile 修饰的,在多线程环境下线程A修改节点的value或 者新增节点的时候是对线程B可见的。
3.ConcurrentHashMap不支持key或者value 为null 的原因 对于key不能为null,估计是作者不喜欢null的key的原因。 为什么value不能是null? 因为ConcorrentHashMap工作于多线程环境,如果ConcurrentHashMap.get(key)返回null, 就无法判断值是null, 还是没有该key;而单线程的HashMap却可以用containsKey(key)判断是否包含了这个key。
4.ConcurrentHashMap迭代器是强-致性还 是弱一致性 与HashMap迭代器是强一致性不同,ConcurrentHashMap 迭代器是弱一致性。 ConcurrentHashMap的迭代器创建后,就会按照哈希表结构遍历每个元素,但在遍历过程中,内部元素可能会发生变化,如果变化发生在已遍历过的部分,迭代器就不会反映出来,而如果变化发生在未遍历过的部分,迭代器就会发现并反映出来,这就是弱一致性。这样迭代器线程可以使用原来老的数据,而写线程也可以并发的完成改变,保证了多个线程并发执行的连续性和扩展性,是性能提升的关键。
5.JDK7与JDK8中ConcurrentHashMap 的区别 底层数据结构: JDK7底层 数据结构是使用Segment组织的数组+链表,JDK8中取而代之的是数组+链表+红黑树的结构,在链表节点数量大于8 (且数据总量大于等于64)时,会将链表转化为红黑树进行存储。查询时间复杂度:从JDK7的遍历链表0(n), JDK8 变成遍历红黑树O(logN)。保证线程安全机制: JDK7 采用Segment的分段锁机制实现线程安全,其中Segment继承自ReentrantLock。JDK8采用CAS+synchronized保证线程安全。锁的粒度: JDK7 是对需要进行数据操作的Segment加锁,JDK8 调整为对每个数组元素的头节点加锁。
7.JDK8中为什么使用synchronized替换ReentrantLock synchronized性能提升,在JDK6中对synchronized锁的实现引入了大量的优化,会从无锁->偏向锁->轻量级锁->重量级锁一步步转换就是锁膨胀的优化。以及有锁的粗化锁消除自适应自旋等优化。提升并发度和减少内存开销,CAS + synchronized方式时加锁的对象是每个链条的头结点,相对Segment再次提高了并发度。如果使用可重入锁达到同样的效果,则需要大量继承自ReentrantL ock的对象,造成巨大内存浪费。
8.ConcurrentHashMap的并发度如何设计的 并发度可以理解为程序运行时能够同时更新ConccurentHashMap且不产生锁竞争的最大线程数。在JDK7中,实际上就ConcurrentHashMap中的分段锁个数,即Segment]的数组长度, 默认是16,这个值可以在构造函数中设置。如果自己设置了并发度,ConcurrentHashMap 会使用大于等于该值的最小的2的幂指数作为实际并发度。如果并发度设置的过小,会带来严重的锁竞争问题;如果并发度设置的过大,原本位于同一个Segment内的访问会扩散到不同的Segment中,从而引起程序性能下降。在JDK8中,已经摒弃了Segment的概念,选择了Node数组t链表+红黑树结构,并发度大小依赖于数组的大小。
9.ConcurrentHashMap和Hashtable的效率哪个更高 ConcurrentHashMap的效率要高于Hashtable 因为Hashtable给整个哈希表加锁从而实现线程安全。而ConcurrentHashMap的锁粒度更低:在JDK7中采用Segment锁(分段锁)实现线程安全 在JDK8中采用CAS+synchronized实现线程安全。
10.多线程下安全的操作Map还有其它方法吗? 可以使用Collections.synchronizedMap(Map类型的对象)方法进行加同步锁。把对象转换成SynchronizedMap<K, V>类型如果传入的是HashMap对象,其实也是对HashMap做的方法做了一层包装,里面使用对象锁来保证多线程场景下,线程安全,本质也是对HashMap进行全表锁。在竞争激烈的多线程环境下性能依然也非常差,不推荐使用
|