CurrentHashMap源码学习笔记

由于HashMap是线程不安全的在多线程并发下容易出现问题,比如自身的死锁问题,而HashTable虽然是线程安全的,但是由于它的内部是通过对所有操作方法进行方法级别的synchronized加锁,导致性能太低(synchronized太过重量级),所以引入CurrentHashMap

jdk1.7

采用segment分段枷锁机制来控制并发

  • segment[] 分段锁数组

  • segment – 多个hashMap –> HashEntry

  • 多个小的HashMap公用同一把锁

  • put时先确定放到那个segment中然后再确定放到改segment中的那个数组中剩下的和jdk7中的hashMap一样

    jdk1.8

    基于CAS的乐观锁实现来控制并发

  • 思路:
    对于数组上的链表 任何操作都需要通过拿到头节点才能进行后续操作,所以只对头节点进行加锁效率更高

  • 源码分析put

    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
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    final V putVal(K key, V value, boolean onlyIfAbsent) {
    //key不能为null
    if (key == null || value == null) throw new NullPointerException();
    //进行hash计算
    int hash = spread(key.hashCode());
    int binCount = 0;
    for (Node<K,V>[] tab = table;;) {
    Node<K,V> f; int n, i, fh;
    //table为空则初始化
    if (tab == null || (n = tab.length) == 0)
    tab = initTable();
    //table不为空 但是该位置元素为空
    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;
    // 加锁 f就是当前数组链表的头节点 *重点*
    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;
    }