首页 > 其他分享 >【博学谷学习记录】超强总结,用心分享 | HashMap、HashTable、ConcurrentHashMap

【博学谷学习记录】超强总结,用心分享 | HashMap、HashTable、ConcurrentHashMap

时间:2022-11-07 17:44:22浏览次数:45  
标签:扩容 ConcurrentHashMap HashMap 链表 JDK8 线程 HashTable

HashMap 和 HashTable 的区别

1、HashMap 是非线程安全的,HashTable 是线程安全的。

2、HashMap 的键和值都允许有 null 值存在,而 HashTable 则不行。

3、因为线程安全的问题,HashMap 效率比 HashTable 的要高。

4、Hashtable 是同步的,而 HashMap 不是。因此,HashMap 更适合于单线程环境,而 Hashtable 适合于多线程环境。一般现在不建议用 HashTable, ①是 HashTable 是遗留类,内部实现很多没优化和冗余。②即使在多线程环境下,现在也有同步的 ConcurrentHashMap 替代,没有必要因为是多线程而用HashTable。

HashTable 和 ConcurrentHashMap(JDK1.8) 的区别

HashTable 是线程安全的,使用的是 Synchronized 关键字修饰,每次要锁住整个结构,并发性低。

JDK1.8ConcurrentHashMap 取消了Segment 分段锁,采用 CAS 和 synchronized 来保证并发安全。数据结构跟 HashMap1.8的结构类似,数组+链表/红黑二叉树。synchronized 只锁定当前链表或红黑二叉树的首节点,这样只要 hash 不冲突,就不会产生并发,效率又提升 N 倍。

Hashtable对get/put/remove都使用了同步操作。ConcurrentHashMap只对put/remove同步。

Hashtable是快速失败的,遍历时改变结构会报错ConcurrentModificationException。ConcurrentHashMap是安全失败,允许并发检索和更新。

JDK1.8的ConcurrentHashMap和JDK1.7ConcurrentHashMap有什么区别?

  1. JDK8中新增了红黑树

    提高检索时间,在链表长度大于8的时候,将后面的数据存在红黑树中,以加快检索速度。复杂度变成O(logn)

  2. JDK7中使用的是头插法,JDK8中使用的是尾插法

    JDK1.7是用单链表进行的纵向延伸,当采用头插法就是能够提高插入的效率,但是也会容易出现逆序且环形链表死循环问题。但是在JDK1.8之后是因为加入了红黑树使用尾插法,能够避免出现逆序且链表死循环的问题。

  3. JDK7中使用了分段锁,而JDK8中没有使用分段锁了,采用 CAS 和 synchronized 来保证并发安全。

  4. JDK7中使用了ReentrantLock,JDK8中没有使用ReentrantLock了,而使用了Synchronized(锁粒度降低了)

  5. JDK7中的扩容是每个Segment内部进行扩容,不会影响其他Segment,而JDK8中的扩容和HashMap的扩容类似,只不过支持了多线程扩容,并且保证了线程安全

JDK1.8中的ConcurrentHashMap为什么使用synchronized来进行加锁?

JDK8中使用synchronized加锁时,是对链表头结点和红黑树根结点来加锁的,而ConcurrentHashMap会保证,数组中某个位置的元素一定是链表的头结点或红黑树的根结点,所以JDK8中的ConcurrentHashMap在对某个桶进行并发安全控制时,只需要使用synchronized对当前那个位置的数组上的元素进行加锁即可,对于每个桶,只有获取到了第一个元素上的锁,才能操作这个桶,不管这个桶是一个链表还是红黑树。


相比于JDK7中使用ReentrantLock来加锁,因为JDK7中使用了分段锁,所以对于一个ConcurrentHashMap对象而言,分了几段就得有几个ReentrantLock对象,表示得有对应的几把锁。而JDK8中使用synchronized关键字来加锁就会更节省内存,并且jdk也已经对synchronized的底层工作机制进行了优化,效率更好。

ConcurrentHashMap是如何保证并发安全的?

JDK8中ConcurrentHashMap是通过synchronized+cas来实现了。在JDK8中只有一个数组,就是Node数组,Node就是key,value,hashcode封装出来的对象,和HashMap中的Entry一样,在JDK8中通过对Node数组的某个index位置的元素进行同步,达到该index位置的并发安全。同时内部也利用了CAS对数组的某个位置进行并发安全的赋值。

JDK1.8中的ConcurrentHashMap扩容

JDK8中是支持多线程扩容的,JDK8中的ConcurrentHashMap不再是分段,需要扩容时,首先会生成一个双倍大小的数组,之后线程就会进行链表的迁移。

  • 扩容条件:Node 数组满 3/4 时就会扩容
  • 扩容单位:以链表为单位从后向前迁移链表,迁移完成的将旧数组头节点替换为 ForwardingNode
  • 扩容时并发 get
    • 根据是否为 ForwardingNode 来决定是在新数组查找还是在旧数组查找,不会阻塞
    • 如果链表长度超过 1,则需要对节点进行复制(创建新节点),怕的是节点迁移后 next 指针改变
    • 如果链表最后几个元素扩容后索引不变,则节点无需复制
  • 扩容时并发 put
    • 如果 put 的线程与扩容线程操作的链表是同一个,put 线程会阻塞
    • 如果 put 的线程操作的链表还未迁移完成,即头节点不是 ForwardingNode,则可以并发执行
    • 如果 put 的线程操作的链表已经迁移完成,即头结点是 ForwardingNode,则可以协助扩容

JDK1.7中的ConcurrentHashMap扩容

JDK7中也是支持多线程扩容的,原因是,JDK7中的ConcurrentHashMap分段了,每一段叫做Segment对象,每个Segment对象相当于一个HashMap,分段之后,对于ConcurrentHashMap而言,能同时支持多个线程进行操作,前提是这些操作的是不同的Segment,而ConcurrentHashMap中的扩容是仅限于本Segment,也就是对应的小型HashMap进行扩容,所以是可以多线程扩容的。
每个Segment内部的扩容逻辑和HashMap中一样。

标签:扩容,ConcurrentHashMap,HashMap,链表,JDK8,线程,HashTable
From: https://www.cnblogs.com/Azureblue/p/16866817.html

相关文章

  • 关于concurrenthashmap 的 key 和 value不能为空的解释
    在学习源码的时候,看到了concurrenthashmap中的value不能为空的情况,突然觉得很奇怪publicVput(Kkey,Vvalue){returnputVal(key,value,false);}......
  • HashMap与ConcurrentHashMap
    HashMap与ConcurrentHashMap今天查看 webSocket结合redis 写的消息订阅与发布的服务端代码时,发现用ConcurrentHashMap存储session对象,由此引发一些思考;1、常用的has......
  • hashtable和hashmap的区别
    1、两者继承的类不同hashtable继承dictionary类,hashmap继承abstractHashMap类,2、两者提供的接口不同3、两者对null处理不同hashmap中key不能为null,但是value可以为null......
  • HashMap 的 7 种遍历方式与性能分析!
    参考来自于:HashMap的7种遍历方式与性能分析!方法之1:使用forEachpublicclassHashMapTest{publicstaticvoidmain(String[]args){//创建并赋值......
  • Java的HashSet和HashMap性能选项
    Java的HashSet和HashMap性能选项1.*HashSet和HashMap的性能选项对于HashSet及其子类而言,它们采用hash算法来决定集合中元素的存储位置,并通过hash算法来控制集合的大小;对......
  • 数学 动规 滑动窗口 HashMap里放数组 dfs 暴力
    1比特与2比特字符intn=bits.length;inti=0;因为,如果最后一个字符必须是一个一比特字符,那么,一定可以跳到最会一个位置。也就是n-1这个位置。所以不能遍......
  • JAVA并发容器-ConcurrentHashMap 1.7和1.8 源码解析
    HashMap是一个线程不安全的类,在并发情况下会产生很多问题,详情可以参考​​HashMap源码解析​​;HashTable是线程安全的类,但是它使用的是synchronized来保证线程安全,线程竞争......
  • HashMap 源码解析
    源码学习,边看源码边加注释,边debug,边理解。基本属性常量DEFAULT_INITIAL_CAPACITY:默认数组的初始容量-必须是2的幂。MAXIMUM_CAPACITY:数组的最大容量DEFAULT_LOAD_FACTOR:哈......
  • JAVA++:HashMap无序?TreeMap有序?
    书上说HashMap是无序的,TreeMap是有序的(有序无序是针对key的),但是实际去敲的时候发现不是这样,有时HashMap是有序的,有时TreeMap是无序的。于是就做了以下测试来探究:......
  • TreeMap,HashMap,LinkedHashMap区别
    TreeMap,HashMap,LinkedHashMap之间的区别和TreeSet,HashSet,LinkedHashSet之间的区别相似。TreeMap:内部排序,内部使用了红黑树排序HashMap:无序。LinkedHashMap:顺序存取,内部......