引言

ConcurrentHashMap的数据结构奠定了其高并发编程中的作用。其基本的数据结构还是离不开其本源HashMap。比如JDK8中他们同样用了数组+链表+红黑树,只不过在ConcurrentHashMap中增加了一些无锁技术,来实现多线程操作容器。

JDK7

先贴两张盗的图(实在不想画图,就找了两张喜欢的)
1.7-1.png
1.7-2.png
从图中可以看出,其数据结构主要由Segment数组+HashEntry数组+链表组成,也就是说定位一个数据时,要通过两次Hash计算。

JDK7的ConcurrentHashMap主要通过锁分段技术实现对资源的并发访问。Segment数组中的每个Segment,都是继承自ReentrantLock (可重入锁,后期再写一个锁的专题),即其锁的粒度为Segment。

在一个线成获取到这个Segment的锁的时候,其他线成可以访问别的Segment,以此增加了容器处理的吞吐量。

JDK8

JDK8的HashMap中比JDK7多了一个红黑树以用来增加数据分布的平衡性。JDK8的ConcurrentHashMap相对于JDK7来说,锁的粒度变小了,也就是说并发性更大了。用了synchronized+CAS代替了ReentrantLock ,数据结构也简单了一些。
贴一下盗的图:
1.8-1.png

从图上可以看出,数据结构相对比较简单,主要由数组与链表+红黑树组成,锁的粒度就是各个链表首节点。

总结

  1. 锁粒度:

JDK8的锁粒度相比于JDK7降低了,JDK1.7版本锁的粒度是基于Segment的,包含多个HashEntry,而JDK1.8锁的粒度就是HashEntry(首节点).
2. ##### 代码复杂度
JDK8的虽然去掉了分段锁的概念,即数据结构变得简单了,但是相应的代码复杂度就上来了,比如红黑树
3. ##### 锁的方式
JDK8采用synchronized+CAS代替了ReentrantLock,这块的原因可能就是synchronized比较受重视。

  • 因为粒度降低了,在相对而言的低粒度加锁方式,synchronized并不比ReentrantLock差,在粗粒度加锁中ReentrantLock可能通过Condition来控制各个低粒度的边界,更加的灵活,而在低粒度中,Condition的优势就没有了。
  • 基于JVM的synchronized优化空间更大,使用内嵌的关键字比使用API更加自然。
  • 在大量的数据操作下,对于JVM的内存压力,基于API的ReentrantLock会开销更多的内存。

参考资料

http://ifeve.com/concurrenthashmap/
https://baijiahao.baidu.com/s?id=1617089947709260129&wfr=spider&for=pc
http://www.importnew.com/26049.html
http://www.importnew.com/28263.html