{ ConcurrentHashMap }

  • ConcurrentHashMap-源码分析

    |

    产生原因

    • 在多线程环境下,HashMap在扩容时会产生死循环,而线程安全的HashTable,在涉及多线程的操作都采用Synchronized锁住整个数组表,即所有线程在争夺一个资源,效率很低。

    1.7 vs 1.8

    • 1.7的 ConcurrentHashMap 采用的是分段锁的设计,底层数据结构是Segment + HashEntry,Segment实现了ReentrantLock,自带锁的功能,每次只锁定对应的Segment,多个线程争夺同个segment时,通过tryLock争夺,锁定的粒度下降了,性能也就提高了不少。
    • 1.8的ConcurrentHashMap 摒弃了分段锁的概念(Segment),沿用了HashMap的思想,基于数组 + 链表 + 红黑树,底层采用的Node + CAS + Synchronized,保证并发的安全性,锁住的是粒度更小的Node。

    分析

    内部类Node, 实现了Map.Entry,用于存储键值对,节点用 volatile修饰,保证了多线程间的可见性,同时注意 value也用volatile修饰,无法通过setValue设置value变量。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    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;
    }
    }