1、HashMap
数据结构:数组 + 链表 + 红黑树
安全性:非线程安全,因为底层代码操作数组时未加锁。
应用场景:高并发情况下,put、remove 成员变量时可能产生线程安全问题,需加锁;
操作非成员标量时不会发生线程安全问题,可以不用加锁。
扩容:元素插入后判断数组长度是否超阈,默认阈值0.75,若超阈则进行扩容,扩容大小为原数组的2的幂次方,若原数组所在内存上没有连续的可用空间,则申请新的可用连续空间,将旧数组复制到新的地址,再将旧数组置为null,等待GC回收。
缩容:无动态缩容机制,需手动缩容。
2、concurrentHashMap
数据结构:分段数组 + 链表 + 红黑树
安全性:线程安全,因为底层代码在操作每一个segment时都会对segment加锁,保证线程安全。
应用场景:高并发情况下,线程安全,操作成员变量或局部变量都不需要单独加锁处理。
性能:读取数据时不加锁,高效,且因为map中的value值是添加volatile关键字修饰的,可保证读取到最新值,降低CPU负载。
写入数据时,会先通过hashcode算法算出要写入的segment(桶的位置),然后锁定当前segment,而不是锁定整个数组,所以读写效率比hashTable要高很多。
扩容、缩容:同hashMap
补充:以上是基于jdk7中的分析;jdk8有了些许调整,主要是对concurrentHashMap的加锁方式做了调整,不再是锁定整个segment,而是引入了synchronized关键字针对具体的节点进行加锁。segment是分段数组,里边可能不止一个节点,jdk8中锁的粒度更加精细,应对更加高的并发。
除了加锁粒度不同以外,jdk7和jdk8中对concurrentHashMap的加锁方式也不相同,有兴趣的可自行百度,这里只是简单介绍,不再展开。