java是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由Sun Microsystems公司于1995年5月推出的Java程序设计语言和Java平台(即JavaEE, JavaME, JavaSE)的总称。本站提供基于Java框架struts,spring,hibernate等的桌面应用、web交互及移动终端的开发技巧与资料

保持永久学习的心态,将成就一个优秀的你,来 继续搞起java知识。

一、什么是ConcurrentHashMap?

ConcurrentHashMap是一种线程安全且高效的HashMap。

二、为什么要引入ConcurrentHashMap?

针对Key-Value类型的集合而言,HashMap不是线程安全的,无法在多线程或高并发情况下使用,而Hashtable虽然使用synchronized关键字来保证安全,但是在高并发等线程竞争比较激烈的情况下其效率非常低下。更可怕的是,使用synchronized关键字意味着访问线程不得不竞争同一把锁,不仅不允许其他线程进行put操作,也导致了get操作的阻塞,使得这种竞争越来越激烈。

正是由于以上原理,Java才不得不引入了ConcurrentHashMap。

三、ConcurrentHashMap是如何实现的?

ConcurrentHashMap使用了锁分段技术,它将数据分成一段一段的进行存储,每一段数据被单独分配一把锁。这样,当某一个线程访问某一段数据时,并不影响其他线程访问其他段数据,原因则是基于不同段数据被分配的不同的锁,线程访问不同段数据时并没有锁的竞争,从而可以提高实现高并发的效率。

ConcurrentHashMap的get()方法十分简单和高效。它先通过hash()方法得到key的hash值h,然后根据h的哈希算法得到u,即定位到segment,之后再对u进行一定的运算定位到元素,全程无锁,代码如下:

/**
     * Returns the value to which the specified key is mapped,
     * or {@code null} if this map contains no mapping for the key.
     *
     * <p>More formally, if this map contains a mapping from a key
     * {@code k} to a value {@code v} such that {@code key.equals(k)},
     * then this method returns {@code v}; otherwise it returns
     * {@code null}.  (There can be at most one such mapping.)
     *
     * @throws NullPointerException if the specified key is null
     */
    public V get(Object key) {
        Segment<K,V> s; // manually integrate access methods to reduce overhead
        HashEntry<K,V>[] tab;
        int h = hash(key);
        long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE;
        if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != null &&
            (tab = s.table) != null) {
            for (HashEntry<K,V> e = (HashEntry<K,V>) UNSAFE.getObjectVolatile
                     (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE);
                 e != null; e = e.next) {
                K k;
                if ((k = e.key) == key || (e.hash == h && key.equals(k)))
                    return e.value;
            }
        }
        return null;
    }

那么它是如何实现全程无锁的呢?关键就在于get()方法使用的共享变量均被定义成volatile。而被定义成volatile的变量,能够在线程之间保持可见性,能够被多线程同时读,并能保证不会读取到过期的值,但是只能被单线程写。之所以可以被多线程同时读且不会读取到过期的值,是根据java内存模型的happen before原则,对volatile字段的写入操作优先于读操作。比如,两个线程分别修改和获取volatile变量,get操作始终拿到的是最新的值。

因为水平有限,难免有疏忽或者不准确的地方,希望大家能够直接指出来,我会及时改正。一切为了知识的分享。

后续会有更多的精彩的内容分享给大家。