*
HashMap:
HashMap: 数组 + 链表 /红黑树(链表升级为红黑树条件:当链表长度大于8,数组长度大于64; 红黑树降级为链表条件: 当红黑树高度小于6)
(非线程安全的容器)
- JDK1.7 死循环
- JDK1.8 数据覆盖
JDK1.7死循环分析:
JDK1.7 之所以死循环原因是 resize()中的transfer()方法导致:如下
ConcurrentHashMap:
(线程安全的容器)
ConcurrentHashMap使用分段锁技术(Segment),将数据分成一段一段的存储,然后给每一段数据配一把锁(悲观锁),当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问,能够实现真正的并发访问。
JDK1.8锁优化:
**ConcurrentHashMap 取消了 Segment 分段锁,采用CAS 和 synchronized 来保证并发安全。**数据结构跟 HashMap1.8 的结构类似,数组+链表/红黑二叉树。Java 8 在链表场度超过一定阈值(8)时将链表(寻址时间复杂度为 O(N))转换为红黑树(寻址时间复杂度为 O(log(N))) synchronized 只锁定当前链表或红黑二叉树的盲节点,这样只要 hash 不冲突,就不会产生并发,效率又提升 N 倍。
Hashtable:
(线程安全的容器)
给put方法整体加锁,因此一般情况下不会使用Hashtable。
面试题: HashMap, Hashtable,ConcurrentHashMap 的区别?
-
HashMap是非线程安全的容器,它在 JDK 1.7会造成死循环,JDK 1.8会造成数据覆盖;Hashtable和ConcurrentHashMap都是线程安全。
-
Hashtable 实现线程安全的手段比较简单,它是在 put方法整体加了一把锁,使用synchronized 修饰,因此性能不高,所以使用频率比较低;ConcurrentHashMap是 HashMap在多线程下的替代方案,它在 JDK 1.7的时候使用的 Lock 加分段锁的方案来实现线程安全问题的保障的,而在JDK 1.8 的时候使用了大量的CAS,volatile 来实现线程的,并且在 JDK 1.8 的时候读取的时候不加锁(读取的数据可能不是最新,因为读取和写入可以同时进行),只有在写的时候才加锁。