数据结构

由 Segment数组 + HashEntry 数组 构成 HashEntry 存键值对

一个concurrentHashMap 饱和Segment数组
一个Segmengt包含一个HashEntry数组
一个HashEntry元素是一个链表
锁的获取时在 segment 上的竞争
图示:
image.png

HashEntry

  1. static final class HashEntry<K,V> {
  2. final int hash;
  3. final K key;
  4. volatile V value;
  5. volatile HashEntry<K,V> next;
  6. HashEntry(int hash, K key, V value, HashEntry<K,V> next) {
  7. this.hash = hash;
  8. this.key = key;
  9. this.value = value;
  10. this.next = next;
  11. }

put操作

  1. public V put(K key, V value) {
  2. Segment<K,V> s;
  3. if (value == null)
  4. throw new NullPointerException();
  5. int hash = hash(key);
  6. // 通过位运算确定哪个segment,
  7. int j = (hash >>> segmentShift) & segmentMask;
  8. // 通过unsafe 获取
  9. if ((s = (Segment<K,V>)UNSAFE.getObject
  10. (segments, (j << SSHIFT) + SBASE)) == null)
  11. s = ensureSegment(j);//初始化一个segment
  12. //调用Segment的put方法
  13. return s.put(key, hash, value, false);
  14. }
  1. 先算hash
  2. 确定segment的下表
  3. 获取segment,没有就会创建,CAS的方式获取

    Segment的put方法

    ```java final V put(K key, int hash, V value, boolean onlyIfAbsent) { //对segment加锁 HashEntry node = tryLock() ? null :

    1. scanAndLockForPut(key, hash, value);

    V oldValue; try {

    1. //
    2. HashEntry<K,V>[] tab = table;
    3. int index = (tab.length - 1) & hash;
    4. //获取头结点
    5. HashEntry<K,V> first = entryAt(tab, index);
    6. for (HashEntry<K,V> e = first;;) {//开始遍历first为头结点的链表
    7. if (e != null) {//<1>
    8. //
    9. //依据onlyIfAbsent来判断是否覆盖已有的value值
    10. K k;
    11. if ((k = e.key) == key ||
    12. (e.hash == hash && key.equals(k))) {
    13. oldValue = e.value;
    14. if (!onlyIfAbsent) {
    15. e.value = value;
    16. ++modCount;
    17. }
    18. break;
    19. }
    20. e = e.next;//说明当前e节点对应的key不是需要的
    21. }
    22. else {//头插法
    23. if (node != null)
    24. node.setNext(first);
    25. else
    26. node = new HashEntry<K,V>(hash, key, value, first);
    27. int c = count + 1;
    28. if (c > threshold && tab.length < MAXIMUM_CAPACITY)//判断是否需要进行扩容
    29. //扩容,node插入到新的数组中。
    30. rehash(node);
    31. else
    32. setEntryAt(tab, index, node);
    33. ++modCount;
    34. count = c;
    35. oldValue = null;
    36. break;
    37. }
    38. }

    } finally {

    1. unlock();

    } return oldValue; }

```

  1. 先获取segment的锁
  2. 找到k所在HashEntry 数组的下表
  3. 获取这个HashEntry 的头节点
  4. 开始遍历
  5. 采用头插法