ConcurrentHashMap1.7和ConcurrentHashMap1.8的异同

ConcurrentHashMap

在这里插入图片描述
ConcurrentHashMap在与HashMap的不同之处在于多线程下更安全,而针对于Hashtable的基础上进行了一次升级。Hashtable的安全机制过于粗暴,导致性能很低。根据不同版本采用的方式也有不同。

public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
    implements ConcurrentMap<K,V>, Serializable

1.7版本的ConcurrentHashMap

主要采用的是分段锁机制,并且读取时不进行加锁操作。底层的结构还是和1.7版本的hashmap类似,只是多了一个Segments数组,一个Segment可以看作类似的hashtable,里面包含了一个table数组用来存取hashentry,table里每个元素代表着链表的头节点,transient表示该变量不进行序列化,继承了ReentrantLock 重入锁。

static final class Segment<K,V> extends ReentrantLock implements Serializable {
    
     
    transient volatile int count; 
    transient int modCount; 
    transient int threshold; 
    transient volatile HashEntry<K,V>[] table; 
    final float loadFactor; 
}

HashEntry的声明类,用于数据存取的对象

static final class HashEntry<K,V> {
    
     
    final K key; //键
    final int hash; //hash值hashcode(k)^hashcode(v)
    volatile V value; 
    final HashEntry<K,V> next; //final修饰的变量只能被初始化一次
}

ConcurrentHashMap的实现:

public ConcurrentHashMap(int initialCapacity, 
                         float loadFactor, int concurrencyLevel) {
    
     
    if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0) 
        throw new IllegalArgumentException(); 
    if (concurrencyLevel > MAX_SEGMENTS) 
        concurrencyLevel = MAX_SEGMENTS; 
    // Find power-of-two sizes best matching arguments 
    int sshift = 0; 
    int ssize = 1; 
    while (ssize < concurrencyLevel) {
    
     
        ++sshift; 
        ssize <<= 1; 
    } 
    segmentShift = 32 - sshift; 
    segmentMask = ssize - 1; 
    this.segments = Segment.newArray(ssize); 
    if (initialCapacity > MAXIMUM_CAPACITY) 
        initialCapacity = MAXIMUM_CAPACITY; 
    int c = initialCapacity / ssize; 
    if (c * ssize < initialCapacity) 
        ++c; 
    int cap = 1; 
    while (cap < c) 
        cap <<= 1; 
    for (int i = 0; i < this.segments.length; ++i) 
        this.segments[i] = new Segment<K,V>(cap, loadFactor); 
}

获取数据V不需要进行加锁,首先找到哪一个segment,然后确定table[i]数组中的位置,在进行遍历链表,取值。

1 public V get(Object key) {
    
     
2     int hash = hash(key.hashCode()); //采用二次hash减少hash冲突
3     return segmentFor(hash).get(key, hash); 
4 }
final Segment<K,V> segmentFor(int hash) {
    
     
     return segments[(hash >>> segmentShift) & segmentMask]; 
}
 1 V get(Object key, int hash) {
    
     //get方法
 2     if (count != 0) {
    
     // read-volatile // ①
 3         HashEntry<K,V> e = getFirst(hash); 获取table里的元素
 4         while (e != null) {
    
     
 5             if (e.hash == hash && key.equals(e.key)) {
    
     
 6                 V v = e.value; 
 7                 if (v != null) // ② 注意这里
 8                     return v; 
 9                 return readValueUnderLock(e); // recheck 
10             } 
11             e = e.next; 
12         } 
13     } 
14     return null; 
15 }

在这里需要注意的是next指向下一个结点被final关键字修饰,也就是说不能在直接进行插入或者删除的工作,而且添加元素也只能在头部进行插入。当我们正在查询时,此时进行了添加工作,应该优先进行添加工作,happen-before原则。这样能保证我们读取的值是最新的。在创建entry时会发现很多变量都被volatile和final修饰,volatile主要是保证线程的有序性和可见性。
put操作源码

 1 V put(K key, int hash, V value, boolean onlyIfAbsent) {
    
     
 2     lock(); //获取锁的对象
 3     try {
    
     
 4         int c = count; 
 5         if (c++ > threshold) // ensure capacity 扩容
 6             rehash(); 
 7         HashEntry<K,V>[] tab = table; 
 8         int index = hash & (tab.length - 1); //获取数组下标值
 9         HashEntry<K,V> first = tab[index]; //数组中第一个entry对象
10         HashEntry<K,V> e = first; 
11         while (e != null && (e.hash != hash || !key.equals(e.key))) 
12             e = e.next; 
13    
14         V oldValue; 
15         if (e != null) {
    
     //表示数组已经存在该KEY
16             oldValue = e.value; 
17             if (!onlyIfAbsent) //表示是否要覆盖
18                 e.value = value; 
19         } 
20         else {
    
     //不存在key,
21             oldValue = null; 
22             ++modCount; 
23             tab[index] = new HashEntry<K,V>(key, hash, first, value); 
24             count = c; // write-volatile 
25         } 
26         return oldValue; 
27     } finally {
    
     
28         unlock(); 
29     } 
30 }

remove操作详情

 1 V remove(Object key, int hash, Object value) {
    
     
 2     lock(); //获取锁对象
 3     try {
    
     
 4         int c = count - 1; 
 5         HashEntry<K,V>[] tab = table; 
 6         int index = hash & (tab.length - 1); 
 7         HashEntry<K,V> first = tab[index]; 
 8         HashEntry<K,V> e = first; 
 9         while (e != null && (e.hash != hash || !key.equals(e.key))) 
10             e = e.next; 
11    
12         V oldValue = null; 
13         if (e != null) {
    
     
14             V v = e.value; 
15             if (value == null || value.equals(v)) {
    
     
16                 oldValue = v; 
17                 // All entries following removed node can stay 
18                 // in list, but all preceding ones need to be 
19                 // cloned. 
20                 ++modCount; 
21                 HashEntry<K,V> newFirst = e.next; 
22                 for (HashEntry<K,V> p = first; p != e; p = p.next) 
23                     newFirst = new HashEntry<K,V>(p.key, p.hash, 
24                                                   newFirst, p.value); //方法里的newFirst表示next指针。
25                 tab[index] = newFirst; 
26                 count = c; // write-volatile 
27             } 
28         } 
29         return oldValue; 
30     } finally {
    
     
31         unlock(); //解锁释放锁的对象
32     } 
33 }

由于删除之后表结构会发生该表,会将删除结点之前的元素进行一个倒叙排列,最后再将结点之后的元素连接起来。

ConcurrentHashMap1.8

ConcurrentHashMap1.8底层采用的数据结构是数组+链表+红黑树,采用的synchronization+CAS实现多线程安全机制。
Node结点代码里只有find并没有其他方法!

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

        Node(int hash, K key, V val, Node<K,V> next) {
    
    
            this.hash = hash;
            this.key = key;
            this.val = val;
            this.next = next;
        }

        public final K getKey()       {
    
     return key; }
        public final V getValue()     {
    
     return val; }
        public final int hashCode()   {
    
     return key.hashCode() ^ val.hashCode(); }
        public final String toString(){
    
     return key + "=" + val; }
        public final V setValue(V value) {
    
    
            throw new UnsupportedOperationException();
        }

        public final boolean equals(Object o) {
    
    
            Object k, v, u; Map.Entry<?,?> e;
            return ((o instanceof Map.Entry) &&
                    (k = (e = (Map.Entry<?,?>)o).getKey()) != null &&
                    (v = e.getValue()) != null &&
                    (k == key || k.equals(key)) &&
                    (v == (u = val) || v.equals(u)));
        }

        /**
         * Virtualized support for map.get(); overridden in subclasses.
         */
        Node<K,V> find(int h, Object k) {
    
    
            Node<K,V> e = this;
            if (k != null) {
    
    
                do {
    
    
                    K ek;
                    if (e.hash == h &&
                        ((ek = e.key) == k || (ek != null && k.equals(ek))))
                        return e;
                } while ((e = e.next) != null);
            }
            return null;
        }
    }

一些compareAndSwapObject(CAS)操作,保证了原子性

    @SuppressWarnings("unchecked")
    static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
    
    
        return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
    }

    static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
                                        Node<K,V> c, Node<K,V> v) {
    
    
        return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
    }

    static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
    
    
        U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
    }

定义了一些变量

扫描二维码关注公众号,回复: 13710788 查看本文章
 transient volatile Node<K,V>[] table;

    /**
     * The next table to use; non-null only while resizing.
     */
    private transient volatile Node<K,V>[] nextTable;

    /**
     * Base counter value, used mainly when there is no contention,
     * but also as a fallback during table initialization
     * races. Updated via CAS.
     */
    private transient volatile long baseCount;

    /**
     * Table initialization and resizing control.  When negative, the
     * table is being initialized or resized: -1 for initialization,
     * else -(1 + the number of active resizing threads).  Otherwise,
     * when table is null, holds the initial table size to use upon
     * creation, or 0 for default. After initialization, holds the
     * next element count value upon which to resize the table.
     */
    private transient volatile int sizeCtl;

    /**
     * The next table index (plus one) to split while resizing.
     */
    private transient volatile int transferIndex;

    /**
     * Spinlock (locked via CAS) used when resizing and/or creating CounterCells.
     */
    private transient volatile int cellsBusy;

    /**
     * Table of counter cells. When non-null, size is a power of 2.
     */
    private transient volatile CounterCell[] counterCells;

    // views
    private transient KeySetView<K,V> keySet;
    private transient ValuesView<K,V> values;
    private transient EntrySetView<K,V> entrySet;

get方法,并没有进行加锁,支持高并发

public V get(Object key) {
    
    
        Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
        int h = spread(key.hashCode());
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (e = tabAt(tab, (n - 1) & h)) != null) {
    
    
            if ((eh = e.hash) == h) {
    
    
                if ((ek = e.key) == key || (ek != null && key.equals(ek)))
                    return e.val;
            }
            else if (eh < 0)
                return (p = e.find(h, key)) != null ? p.val : null;
            while ((e = e.next) != null) {
    
    
                if (e.hash == h &&
                    ((ek = e.key) == key || (ek != null && key.equals(ek))))
                    return e.val;
            }
        }
        return null;
    }

put方法,通过synchronized 进行加锁

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();
        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();
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
    
    
                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) {
    
    
                            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;
                                    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)
                        treeifyBin(tab, i);
                    if (oldVal != null)
                        return oldVal;
                    break;
                }
            }
        }
        addCount(1L, binCount);
        return null;
    }

猜你喜欢

转载自blog.csdn.net/cout_s/article/details/120597427