美文网首页
面试刷题10:ConcurrentHashMap如何保证线程安全

面试刷题10:ConcurrentHashMap如何保证线程安全

作者: 李福春carter | 来源:发表于2020-03-25 17:32 被阅读0次
    image.png

    <br />
    <br />
    <br />集合框架中的HashTable,Stack,以及同步包装集合在高并发场景下都非常低效,java提供了并发包应对高并发场景。<br />
    <br />
    <br />我是李福春,我在准备面试,今天的问题是?<br />
    <br />java提供了哪些并发的容器?ConcurrentHashMap如何保证线程安全?<br />
    <br />

    java体系中的并发容器

    <br />
    <br />java体系中有如下同步容器:<br />
    <br />1, HashTable,Stack 同步容器,内部使用sychronized关键字保证同步操作;<br />
    <br />2,同步包装器Collections.synchronizedMap(),内部也使用sychronized关键字保证同步操作;

    3,并发包提供的同步容器,比如ConcurrentHashMap , CopyOnWriteArrayList , ArrayBlockingQueue,SynchronizedQueue

    ConcurrentHashMap的同步分析

    <br />
    <br />为何会出现ConcurrenHashMap?<br />
    <br />1, HashTable在高并发场景下性能低下;<br />
    <br />2,HashMap 不是线程安全的容器;<br />
    <br />3,同步包装器虽然使用同步方法快提升了部分性能,但是还是不适合高并发场景下的性能需求;<br />
    <br />
    <br />
    <br />接下来回答问题,ConcurrentHashMap如何保证线程安全?<br />

    java7

    <br />java7版本使用的是分离锁(segment)实际上是一种再入锁(RetrantLock)来保证线程安全;<br />segment的数量是concurrentLevel决定,默认值是16;<br />
    <br />
    <br />扩容的时候是针对单个segment扩容的,写操作也是,修改数据的时候锁定的部分,所以比较高效;<br />
    <br />但是后去size可能不太准确。

    数据结构如下图:<br />

    image.png

    <br />

    java8

    <br />java8中segment依然存在,不过不起结构上的作用,只起到保证序列化的兼容性。<br />
    <br />内部使用volatile来保证数据存储的可见性;<br />
    <br />利用CAS操作,在特定场景下进行无锁并发操作,内部的锁实际用的是syncronized;<br />
    <br />因为在jdk8中,syncronized已经得到性能的优化,并且对比再入锁可以减少内存消耗。<br />
    <br />见代码:<br />

    
    final V putVal(K key, V value, boolean onlyIfAbsent) { if (key == null || value == null) throw new NullPointerException();
        int hash = spread(key.hashCode());
        int binCount = 0;
        for (Node<K,V>[] tab = table;;) {
            Node<K,V> f; int n, i, fh; K fk; V fv;
            if (tab == null || (n = tab.length) == 0)
                tab = initTable();
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
                // 利用CAS去进行无锁线程安全操作,如果bin是空的
                if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value)))
                    break; 
            }
            else if ((fh = f.hash) == MOVED)
                tab = helpTransfer(tab, f);
            else if (onlyIfAbsent // 不加锁,进行检查
                     && fh == hash
                     && ((fk = f.key) == key || (fk != null && key.equals(fk)))
                     && (fv = f.val) != null)
                return fv;
            else {
                V oldVal = null;
                synchronized (f) {
                       // 细粒度的同步修改操作... 
                    }
                }
                // Bin超过阈值,进行树化
                if (binCount != 0) {
                    if (binCount >= TREEIFY_THRESHOLD)
                        treeifyBin(tab, i);
                    if (oldVal != null)
                        return oldVal;
                    break;
                }
            }
        }
        addCount(1L, binCount);
        return null;
    }
    
    

    小结

    <br />本节回答了java提供的并发容器分类,以及ConcurrentHashMap在java7,java8中的是如何保证线程安全的。<br />
    <br />面试官喜欢问分离锁,画一下数据结构就明确了。<br />

    image.png

    原创不易,转载请注明出处。

    相关文章

      网友评论

          本文标题:面试刷题10:ConcurrentHashMap如何保证线程安全

          本文链接:https://www.haomeiwen.com/subject/swyxuhtx.html