ConcurrentHashMap底层实现

ConcurrentHashMap和HashTable区别

  ConcurrentHashMap融合了hashtable和hashMap二者的优势;
  hashTable是做了同步的,hashMap没有同步,所以hashMap在单线程情况下效率高,hashTable在多线程情况下,同步操作能保证程序执行的正确性;
  但是hashTable每次同步执行都要锁住整个结构;

ConcurrentHashMap锁的方式是稍微细粒度的(分段锁机制),ConcurrentHashMap将hash表分为16个桶(默认值);

ConcurrentHashMap jdk1.7和jdk1.8底层实现的区别

  JDK1.7 ConcurrentHashMap
  底层采用:Segment+HashEntry
  底层一个Segment数组,存储一个Segments对象,一个Segments中存储一个Entry数组,存储的每一个Entry对象又是一个链表头结点;

当数据添加时,根据key值找到Segment对应的数据段,然后匹配数据块,采用链表方式进行存储;

JDK1.8 ConcurrentHashMap

底层采用:

  数组+链表+红黑树,类似于HashMap(JDK1.8)
  数组使用用来存放树或者链表的头结点当一个链表中的数量达到一个数目时,会使查询速率降低,所以达到一定阈值时,会将一个链表转换为一个红黑二叉树,通告查询的速率;

ConcurrentHashMap取消了Segment分段锁的机制,从而实现一段数据进行加锁,减少了并发,CAS(读)+synchronized(写)

ConcurrentHashMap底层put()方法

 public V put(K key, V value) {
        return putVal(key, value, false);
    }
  进入putVal()方法:   

    final V putVal(K key, V value, boolean onlyIfAbsent) {
     //判断key和value是否为空,如果为空则报异常
        if (key == null || value == null) throw new NullPointerException();
     //重新计算key的hash值,有效减少hash值冲突
        int hash = spread(key.hashCode());
     //用于记录相应链表的长度
        int binCount = 0;
     //遍历当前数组当中所有的数据
        for (Node<K,V>[] tab = table;;) {
            Node<K,V> f; int n, i, fh;
       //判断数组是否为空
            if (tab == null || (n = tab.length) == 0)
          //如果为空要进行数组的初始化操作
                tab = initTable();
       //根据key的hash值找到位置,如果该位置没有元素
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
          //获取到空的元素,然后重新创建一个新的node放进去
                if (casTabAt(tab, i, null,
                             new Node<K,V>(hash, key, value, null)))
                    break;                   // no lock when adding to empty bin
            }
       //判断当前数组元素状态是否需要扩容
            else if ((fh = f.hash) == MOVED)
                tab = helpTransfer(tab, f);
            else {
          //如果f是头结点
                V oldVal = null;
                synchronized (f) {
            //加锁
                    if (tabAt(tab, i) == f) {
              //头结点的hash值大于0,说明是链表
                        if (fh >= 0) {
                //链表的长度+1
                            binCount = 1;
                            for (Node<K,V> e = f;; ++binCount) {
                                K ek;
                   //判断添加的key和原有key践行hash值判断以及key值判断,如果相等则覆盖
                                if (e.hash == hash &&
                                    ((ek = e.key) == key ||
                                     (ek != null && key.equals(ek)))) {
                                    oldVal = e.val;
                                    if (!onlyIfAbsent)
                                        e.val = value;
                                    break;
                                }
                                Node<K,V> pred = e;
                   //判断当前节点的下一个节点是否为空,如果为空则添加到下一个节点当中
                                if ((e = e.next) == null) {
                                    pred.next = new Node<K,V>(hash, key,
                                                              value, null);
                                    break;
                                }
                            }
                        }
               //判断当前节点是否为红黑树
                        else if (f instanceof TreeBin) {
                            Node<K,V> p;
                            binCount = 2;
                 //如果为红黑树则创建一个树节点
                            if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                           value)) != null) {
                                oldVal = p.val;
                                if (!onlyIfAbsent)
                                    p.val = value;
                            }
                        }
                    }
                }
                if (binCount != 0) {
            //根据当前循环次数判断链表中存在多少个数据,如果数据阈值大于等于8
                    if (binCount >= TREEIFY_THRESHOLD)
              //则进行红黑树转换
                        treeifyBin(tab, i);
                    if (oldVal != null)
                        return oldVal;
                    break;
                }
            }
        }
     addCount(1L, binCount);
        return null;
    }

对于putVal函数的流程大体如下:

    1.判断存储的key,value是否为空,若为空,则抛出异常;

    2.计算key的hash值,随后进入无限循环,该无限循环可以确保成功插入数据,若table表为空或者长度为0,则初始化table表;

    3.根据key的hash值取出table表中的结点元素,若取出的结点元素为空(该桶为空),则使用CAS将key,value,hash值生成的结点放入桶中;

    4.若该节点的hash值为MOVED,则对该桶中的结点进行转移;

    5.对桶中的第一个结点进行加锁,对该桶进行遍历,桶中的结点的hash值与key值与给定的hash值和key值相等,则根据标识选择是否进行更新操作(用给定的value值替换该结点的value值),若遍历完桶仍没有找到hash值与key值和指定的hash值与key值相等的结点,则直接新生一个结点并赋值为之前最后一个结点的下一个结点;

    6.若binCount值达到红黑树转化的阈值,则将桶中的结构转化为红黑树存储,最后,增加binCount的值;

ConcurrentHashMap底层get()方法

public V get(Object key) {
        Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
     //计算key的hash值
        int h = spread(key.hashCode());
     //表不为空并且表的长度大于0并且key所在的桶不为空
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (e = tabAt(tab, (n - 1) & h)) != null) {
       //表中的元素的hash值与key的hash相等
            if ((eh = e.hash) == h) {
          //键相等
                if ((ek = e.key) == key || (ek != null && key.equals(ek)))
                    return e.val;
            }
       //结点hash值小于0
            else if (eh < 0)
          //在桶中查找
                return (p = e.find(h, key)) != null ? p.val : null;
       //对于结点hash值大于0的情况
            while ((e = e.next) != null) {
                if (e.hash == h &&
                    ((ek = e.key) == key || (ek != null && key.equals(ek))))
                    return e.val;
            }
        }
        return null;
    }

get函数根据key的hash值来计算在哪个桶中,再遍历桶,查找元素,若找到则返回该结点,否则,返回null;

原文地址:https://www.cnblogs.com/dabrk/p/12512663.html

时间: 2024-10-21 14:11:38

ConcurrentHashMap底层实现的相关文章

ConcurrentHashMap底层实现原理(JDK1.8)源码分析

ConcurrentHashMap数据结构 ConcurrentHashMap相比HashMap而言,是多线程安全的,其底层数据与HashMap的数据结构相同,数据结构如下: 说明:ConcurrentHashMap的数据结构(数组+链表+红黑树),桶中的结构可能是链表,也可能是红黑树,红黑树是为了提高查找效率. ConcurrentHashMap源码分析 1.类的继承关系 public class ConcurrentHashMap<K,V> extends AbstractMap<K

java多线程:并发包中ConcurrentHashMap和jdk的HashMap的对比

一:HashMap--->底层存储的是Entry<K,V>[]数组--->Entry<K,V>的结构是一个单向的链表static class Entry<K,V> implements Map.Entry<K,V> {        final K key;        V value;        Entry<K,V> next;        int hash; /**         * Creates new entry.

浅析HashMap与ConcurrentHashMap的线程安全性

本文要解决的问题: 最近无意中发现有很多对Map尤其是HashMap的线程安全性的话题讨论,在我的理解中,对HashMap的理解中也就知道它是线程不安全的,以及HashMap的底层算法采用了链地址法来解决哈希冲突的知识,但是对其线程安全性的认知有限,故写这篇博客的目的就是让和我一样对这块内容不熟悉的小伙伴有一个对HashMap更深的认知. 哈希表 在数据结构中有一种称为哈希表的数据结构,它实际上是数组的推广.如果有一个数组,要最有效的查找某个元素的位置,如果存储空间足够大,那么可以对每个元素和内

为什么ConcurrentHashMap是弱一致的

http://ifeve.com/concurrenthashmap-weakly-consistent/ 为什么ConcurrentHashMap是弱一致的 本文将用到Java内存模型的happens-before偏序关系(下文将简称为hb)以及ConcurrentHashMap的底层模型相关的知识.happens-before相关内容参见:JLS §17.4.5. Happens-before Order.深入理解Java内存模型以及Happens before:ConcurrentHash

HashMap\HashTable\ConcurrentHashMap的原理与区别

HashTable: 底层数组+链表,key与value都不能为null,线程安全,实现线程安全的方式是在修改数据时锁住整个HashTable,效率低. 初始size为11,扩容:newsize = oldsize*2 + 1 计算index的方法:index = (hash & 0x7FFFFFFF)%tab.length HashMap: 底层数组+链表实现,可以存储null键和null值,线程不安全 初始size为16,扩容newsize = oldsize*2,size一定为2的n次幂

HashMap、ConcurrentHashMap以及HashTable(面试向)

---->HashMap 在java1.7中,hashmap的数据结构是基于数组+链表的结构,即我们比较熟悉的Entry数组,其包含的(key-value)键值对的形式.在多线程环境下,HashMap进行put操作会引起死循环,是因为多线程会导致HashMap的Entry链表形成环形数据结构,一旦形成环形数据结构,Entry的next节点永远不为空,就会产生死循环获取Entry. hashmap实现原理参考 Entry是HashMap中的一个静态内部类.代码如下 static class Ent

HashMap、Hashtable、ConcurrentHashMap的原理与区别

HashTable 底层数组+链表实现,无论key还是value都不能为null,线程安全,实现线程安全的方式是在修改数据时锁住整个HashTable,效率低,ConcurrentHashMap做了相关优化 初始size为11,扩容:newsize = olesize*2+1 计算index的方法:index = (hash & 0x7FFFFFFF) % tab.length HashMap 底层数组+链表实现,可以存储null键和null值,线程不安全 初始size为16,扩容:newsiz

ConcurrentHashMap和Hashtable区别?

1.底层数据结构: JDK1.7的ConcurrentHashMap底层使用分段的数据+链表实现,JDK1.8采用的数据结构和HashMap1.8的结构一样.数据+链表/红黑二叉树. Hashttable和JDK1.8之前的HashMap的底层数据结构类似都是采用数据+链表的形式,数组是HashMap的主体,链表是为了解决哈希冲突而存在的. 2.实现线程安全的方式(重要): 在JDK1.7的时候,ConcurrentHashMap(分段锁)对整个桶数组进行了分割分段(Segment),每一把锁只

源码分析--ConcurrentHashMap与HashTable(JDK1.8)

ConcurrentHashMap和Hashtable都是线程安全的K-V型容器.本篇从源码入手,简要说明它们两者的实现原理和区别. 与HashMap类似,ConcurrentHashMap底层也是以数组+链表+红黑树实现的,以Node节点封装K-V和hash. static class Node<K,V> implements Map.Entry<K,V> { final int hash; final K key; volatile V val; volatile Node&l