zoukankan      html  css  js  c++  java
  • ConcurrentHashMap底层实现

      1.ConcurrentHashMap和HashTable比较
                ConcurrentHashMap性能高于HashTable,都能够完成线程安全操作,
                Hashtable中线程安全使用synchronized同步方法进行加锁操作,如果当前一个线程正在访问该集合,其他线程是无法进行访问的,需要进行等待
                反之ConcurrentHashMap当中采用分段锁机制(JDK7)
                    
                
            
            2. JDK1.7和JDK1.8底层实现的区别
                 JDK1.7,ConcurrentHashMap使用分段锁技术,将数据分成一段一段的进行村粗,每一个数据段配置一把锁Segment(继承ReentrantLock)
                    底层采用:Segment+HashEntry
                    当数据添加时,根据key值找到Segment对应的数据段,然后匹配数据块,采用链表方式进行存储
                  

        ConcurrentHashMap使用分段锁技术,将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,

        其他段的数据也能被其他线程访问,能够实现真正的并发访问。

        


                   JDK1.8,ConcurrentHashMap取消了Segment分段所的机制,底层采用Node数组+链表+红黑树,从而实现一段数据进行加锁,减少了并发,CAS(读)+synchronized(写)
                    当数据添加时,根据key值找到对应数组的Node,中间采用CAS和synchronized进行数据操作

          CAS是compare and swap的缩写,即我们所说的比较交换。cas是一种基于锁的操作,而且是乐观锁。在java中锁分为乐观锁和悲观锁。

          悲观锁是将资源锁住,等一个之前获得锁的线程释放锁之后,下一个线程才可以访问。而乐观锁采取了一种宽泛的态度,通过某种方式不加锁来处理资源,

          比如通过给记录加version来获取数据,性能较悲观锁有很大的提高。

          CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存地址里面的值和A的值是一样的,那么就将内存里面的值更新成B。

          CAS是通过无限循环来获取数据的,若果在第一轮循环中,a线程获取地址里面的值被b线程修改了,那么a线程需要自旋,到下次循环才有可能机会执行。


                   

        总结:

        

        JDK1.7版本:ReentrantLock+Segment+HashEntry

        JDK1.8版本:synchronized+CAS+HashEntry+红黑树

        1.数据结构:取消了Segment分段锁的数据结构,取而代之的是数组+链表+红黑树的结构。

        2.保证线程安全机制:JDK1.7采用segment的分段锁机制实现线程安全,其中segment继承自ReentrantLock。JDK1.8采用CAS+Synchronized保证线程安全。

        3.锁的粒度:原来是对需要进行数据操作的Segment加锁,现调整为对每个数组元素加锁(Node)。

        4.链表转化为红黑树:定位结点的hash算法简化会带来弊端,Hash冲突加剧,因此在链表节点数量大于8时,会将链表转化为红黑树进行存储。

        5.查询时间复杂度:从原来的遍历链表O(n),变成遍历红黑树O(logN)。

        
            3. ConcurrentHashMap底层put方法实现的核心逻辑

                public V put(K key, V value) {
                    return putVal(key, value, false);
                }
                /** Implementation for put and putIfAbsent */
                final V putVal(K key, V value, boolean onlyIfAbsent) {
                    if (key == null || value == null) throw new NullPointerException();                //判断key和value是否为空,如果为空则报异常
                    int hash = spread(key.hashCode());                                                //重新计算key的hash值,有效减少Hash值冲突
                    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();                                                        //如果为空要进行数组的初始化操作
                        else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {                    //根据key的Hash值找到位置,如果该位置没有元素
                            if (casTabAt(tab, i, null,
                                         new Node<K,V>(hash, key, value, null)))                    //获取到空的元素,然后重新创建一个新的Node放进去
                                break;                   // no lock when adding to empty bin
                        }
                        else if ((fh = f.hash) == MOVED)                                            //判断当前数组元素状态是否需要扩容
                            tab = helpTransfer(tab, f);
                        else {
                            V oldVal = null;
                            synchronized (f) {                                                        //加锁
                                if (tabAt(tab, i) == f) {                                
                                    if (fh >= 0) {
                                        binCount = 1;
                                        for (Node<K,V> e = f;; ++binCount) {
                                            K ek;
                                            if (e.hash == hash &&                                    //判断添加的key和原有key进行Hash值判断以及key值判断,如果相等则覆盖
                                                ((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) {
                                if (binCount >= TREEIFY_THRESHOLD)                                    //根据当前循环次数判断链表中存在多少个数据,如果数据阀值大于等于8
                                                                                                    //则进行红黑树转换
                                    treeifyBin(tab, i);
                                if (oldVal != null)
                                    return oldVal;
                                break;
                            }
                        }
                    }
                    addCount(1L, binCount);                                                            //判断是否需要扩容
                    return null;
                }
            
  • 相关阅读:
    svn cleanup failed–previous operation has not finished 解决方法
    开源SNS社区系统推荐
    从网络获取图片本地保存
    MS SQL Server 数据库连接字符串
    KeepAlive
    Configure Git in debian
    sqlserver query time
    RPi Text to Speech (Speech Synthesis)
    SQL Joins with C# LINQ
    search or reseed identity columns in sqlserver 2008
  • 原文地址:https://www.cnblogs.com/chx9832/p/12510220.html
Copyright © 2011-2022 走看看