ConcurrencyHashMap

141 阅读2分钟

为什么读方法,不需要同步?

因为数据有内存可见性,即加了volatile关键字。有内存可见性之后,只要有线程更新数据,那么其他线程就可以看到最新数据。因为数据被更新:从线程内存——》共享内存。

即1.线程内存2.共享内存。这是本质。//具体来说,就是1.以前线程更新数据,只在当前线程内存可见最新数据。其他线程可能是旧的数据,也可能是新的数据。2.现在,加了可见性关键字,就一定会把新数据从线程内存更新到共享内存,所以其他线程一定可以读最新数据。


代码

/* ---------------- Fields -------------- */

  /**
   * The array of bins. Lazily initialized upon first insertion.
   * Size is always a power of two. Accessed directly by iterators.
   */
  transient volatile Node<K, V>[] table; //有内存可见性。

参考
mp.weixin.qq.com/s/LAWSThrgP…

为什么jdk8 有了cas,还要synchronized?

jdk8的写方法,有cas?干嘛还要同步关键字?
因为要处理两种不同的情况:
1.数据不存在,添加数据 //基于cas,交换null(数组里当前key——》index的value是null)和新数据 2.数据存在,覆盖旧数据的值 /赋值语句,添加同步关键字


代码

/**
   * Implementation for put and putIfAbsent
   * 
   * 写数据的时候,有三种情况
   * 1.第一次,数组为null,所以要初始化数组,然后再写数据(for循环)
   * 2.数据不存在,写数据
   * 3.数据存在,覆盖旧数据的值
   */
  final V putVal(K key, V value, boolean onlyIfAbsent) {
    if (key == null || value == null) { //key和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) { //1.数组为空
        tab = initTable(); //初始化数组,然后for循环,数据不存在
      } else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {//2.数据存在 //添加新的数据,基于cas
        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 { //3.数据不存在 //覆盖旧数据的值,使用同步关键字
        V oldVal = null;
        synchronized (f) { //jdk8-没有使用显式锁,而是直接使用的synchronzied关键字,说明jdk8对synchronized进行了大量的优化
          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;
  }

参考

jdk8源码