Welcome everyone

ConcurrentHashMap 浅析

java 汪明鑫 35浏览 0评论

HashMap,HashTable,ConcurrentHashMap  这三兄弟基本面试必问

工作和学习中也会经常遇到

Jdk中的所有数据结构源码都需要过一遍吗?

我也不知道,反正我不会,因为水平不够,再来是代价太大,

Jdk源码写的还是很牛逼的

我是想起来闲的话源码点进去瞅两眼

 

HashTable的线程安全是通过锁整张table,这样势必会造成性能上的损耗

加锁解锁用脚趾头想都会消耗性能,

而且既然有锁,就一定会有等待,有等待就会造成阻塞,有阻塞,就会降低性能

因此原则上能不用锁就不用锁,如果不能避免使用锁,也要降低锁的粒度,也可以在性能上做一些优化

包括ArrayList,Vector都是列表,Vector是线程安全的,但是他的性能就不如ArrayList

HashMap的源码以前简单的看过,这个源码记得去年面试的时候回回问,我都快会背了。。。

HashMap是线程不安全的,可以通过Collections工具类转为线程安全的

 

ConcurrentHashMap和HashTable都是线程安全的,那为什么从来没见过HashTable这老哥被人使用过呢

因为ConcurrentHashMap是粒度更细的锁,而HashTable一锁就是锁整张表,这谁顶得住

早期版本 ConcurrentHashMap是通过分段锁来降低锁的粒度,

把整张表分成一段段的segment,操作时定位到一个segment,只锁这一个segment,其他segment同样可以操作

在一个segment可以支持读写兵法,不支持写写兵法

不同segment无所谓,随便操作,不影响

ConcurrentHashMap当中每个Segment各自持有一把锁。在保证线程安全的同时降低了锁的粒度,让并发操作效率更高。

 

 

Get方法:

1.为输入的Key做Hash运算,得到hash值。

2.通过hash值,定位到对应的Segment对象

3.再次通过hash值,定位到Segment当中数组的具体位置。

Put方法:

1.为输入的Key做Hash运算,得到hash值。

2.通过hash值,定位到对应的Segment对象

3.获取可重入锁

4.再次通过hash值,定位到Segment当中数组的具体位置。

5.插入或覆盖HashEntry对象。

6.释放锁。

 

 

JDK 1.8之后,ConcurrentHashMap是基于CAS实现的

瞅一眼源码

 

核心静态内部类Node,和HashMap的差不多

static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        volatile V val;
        volatile Node<K,V> next;

 

volatile修饰table,保证可见性

/**
     * The array of bins. Lazily initialized upon first insertion.
     * Size is always a power of two. Accessed directly by iterators.
     */
    transient volatile Node<K,V>[] table;

 

核心方法 putVal

final V putVal(K key, V value, boolean onlyIfAbsent) {
      //不允许为空
        if (key == null || value == null) throw new NullPointerException();
        //key ---> hash  打散
        int hash = spread(key.hashCode());
        int binCount = 0;

    //遍历table
        for (Node<K,V>[] tab = table;;) {
            Node<K,V> f; int n, i, fh;
            
            //初始化table
            if (tab == null || (n = tab.length) == 0)
                tab = initTable();

      //如果hash路由到的位置是空的,则利用cas无锁新增结点
      //i表示路由到的位置
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
                //利用CAS
                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 {
                V oldVal = null;
                synchronized (f) {  //细粒度加锁
                    if (tabAt(tab, i) == f) {
                        if (fh >= 0) {  //fh就是f的hash
                            binCount = 1;
                            for (Node<K,V> e = f;; ++binCount) {
                                K ek;
                                if (e.hash == hash &&
                                    ((ek = e.key) == key ||
                                     (ek != null && key.equals(ek)))) {
                                    oldVal = e.val;  //记录旧值
                                    if (!onlyIfAbsent)
                                        e.val = value;  //key、hash都相同,则值覆盖
                                    break;
                                }
                                Node<K,V> pred = e; //否则创建新的node,并插在链表尾部
                                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)
                        treeifyBin(tab, i);
                    if (oldVal != null)
                        return oldVal;
                    break;
                }
            }
        }
        addCount(1L, binCount);
        return null;
    }

 

初始化table

/**
     * Initializes table, using the size recorded in sizeCtl.
     */
    private final Node<K,V>[] initTable() {
        Node<K,V>[] tab; int sc;
        while ((tab = table) == null || tab.length == 0) {
            // 如果发现冲突,进行spin等待
            if ((sc = sizeCtl) < 0)
                Thread.yield(); // lost initialization race; just spin
      
            // CAS成功返回true,则进入真正的初始化逻辑
            else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {  
                try {
                    if ((tab = table) == null || tab.length == 0) {
                        int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
                        @SuppressWarnings("unchecked")
                        Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
                        table = tab = nt;
                        sc = n - (n >>> 2);
                    }
                } finally {
                    sizeCtl = sc;
                }
                break;
            }
        }
        return tab;
    }

 

水平原因先到这里,后续有空再接着研究源码, 然后安排时间系统学习下 CAS

 

 

 

 

转载请注明:汪明鑫的个人博客 » ConcurrentHashMap 浅析

喜欢 (0)

说点什么

您将是第一位评论人!

提醒
avatar
wpDiscuz