HASMAP结构图

HASMAP结构图_第1张图片

为什么HashMap是线程不安全的

1.如果多个线程同时使用put方法添加元素会丢失元素

假设正好存在两个put的key发生了碰撞,那么根据HashMap的实现,这两个key会添加到数组的同一个位置,这样最终就会发生其中一个线程的put的数据被覆盖。

2.多线程同时扩容会造成死循环

多线程同时检查到扩容,并且执行扩容操作,在进行rehash的时候会造成闭环链表,从而在get该位置元素的时候,程序将会进入死循环。【证明HashMap高并发下问题会在以后的文章中出现】

如何让HashMap实现线程安全?

  1. 直接使用Hashtable
  2. Collections.synchronizeMap方法
  3. 使用ConcurrentHashMap

总结

  1. HashMap 在第一次 put 时初始化,类似 ArrayList 在第一次 add 时分配空间。
  2. HashMap 的 bucket 数组大小一定是2的n次方
  3. HashMap 在 put 的元素数量大于 Capacity * LoadFactor(默认16 * 0.75) 之后会进行扩容
  4. 负载因子是可以修改的,也可以大于1,但是建议不要轻易修改,除非情况非常特殊
  5. JDK8处于提升性能的考虑,在哈希碰撞的链表长度达到TREEIFY_THRESHOLD(默认8)后,会把该链表转变成树结构
  6. JDK8在 resize 的时候,通过巧妙的设计,减少了 rehash 的性能消耗
  7. 扩容是一个特别耗性能的操作,所以当在使用HashMap的时候,估算map的大小,初始化的时候给一个大致的数值,避免map进行频繁的扩容

HASMAP结构图_第2张图片

java1.8在hashmap的node长度大于8的时候,链表就会装化成红黑树的数据结构来存储节点

 

HASMAP结构图_第3张图片

 

java7ConcurrenHashMap相比HashTable,多了一层segment数组,由于有了这一层,同步锁的粒度更细,每次的操作只对其中某个segment加锁,其他segment的节点是可操作的.而在java8中直接对链表和红黑树加锁,去掉了segment这一层。

你可能感兴趣的:(java)