并发容器ConcurrentHashMap( 三 )

get 方法public V get(Object key) {Node[] tab; Node e, p; int n, eh; K ek;int h = spread(key.hashCode());// 检查 table 是否存在 , hashCode 所在的索引是有为空if ((tab = table) != null}// 按照树的方式遍历 Node 查找else if (eh < 0)return (p = e.find(h, key)) != null ? p.val : null;// 按照链表的方式遍历 Node 查找while ((e = e.next) != null) {if (e.hash == h}}return null;}总结一下 get 的过程:

  • 计算 Hash 值 , 并由此值找到对应的槽点;
  • 如果数组是空的或者该位置为 null , 那么直接返回 null 就可以了;
  • 如果该位置处的节点刚好就是我们需要的 , 直接返回该节点的值;
  • 如果该位置节点是红黑树或者正在扩容 , 就用 find 方法继续查找;
  • 否则那就是链表 , 就进行遍历链表查找
put 方法说明public V put(K key, V value) {return putVal(key, value, false);}final V putVal(K key, V value, boolean onlyIfAbsent) {// 计算 key 的 hashCodeint hash = spread(key.hashCode());int binCount = 0;// 循环直到插入成功 ,for (Node[] tab = table;;) {Node f; int n, i, fh;if (tab == null || (n = tab.length) == 0)// 初始化 tabletab = initTable();// tabAt 调用 getObjectVolatile// 当前位置为空可以直接插入的情况else if ((f = tabAt(tab, i = (n - 1)}// 下面是位置已经有值的情况// MOVED 表示当前 Map 正在进行扩容else if ((fh = f.hash) == MOVED)// 帮助进行扩容 , 然后进入下一次循环尝试插入tab = helpTransfer(tab, f);// 未在扩容的情况else {V oldVal = null;// 对f加锁 , f 是存储在当前位置的 Node 的头节点synchronized (f) {// 双重检查 , 保证 Node 头节点没有改变if (tabAt(tab, i) == f) {if (fh >= 0) {// 对链表进行操作binCount = 1;for (Node e = f;; ++binCount) {// 更新值(判断 onlyIfAbsent)或插入链表尾部 ...break;}}else if (f instanceof TreeBin) {// 对树进行操作 ...}}}// 判断是否需要 treeifyif (binCount != 0) {if (binCount >= TREEIFY_THRESHOLD)treeifyBin(tab, i);if (oldVal != null)return oldVal;break;}}}addCount(1L, binCount);return null;}总结一下 put 的过程:
  • 判断Node[] 数组是否初始化 , 没有则进行初始化操作
  • 通过 hash 定位数组的索引坐标 , 是否有 Node 节点 , 如果没有则使用 CAS 进行添加(链表的头节点) , 添加失败则进入下次循环 。
  • 检查到内部正在扩容 , 就帮助它一块扩容 。
  • 如果 f != null, 则使用 synchronized 锁住 f 元素(链表/红黑二叉树的头元素)如果是 Node (链表结构)则执行链表的添加操作如果是 TreeNode (树形结构)则执行树添加操作 。
  • 判断链表长度已经达到临界值 8, 当然这个 8 是默认值 , 大家也可以去做调整 , 当节点数超过这个值就需要把链表转换为树结构 。
tryPresize方法private final void tryPresize(int size) {//计算扩容的目标sizeint c = (size >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY :tableSizeFor(size + (size >>> 1) + 1);int sc;while ((sc = sizeCtl) >= 0) {Node[] tab = table; int n;//tab没有初始化if (tab == null || (n = tab.length) == 0) {n = (sc > c) ? sc : c;//初始化之前 , CAS设置sizeCtl=-1if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {try {if (table == tab) {@SuppressWarnings("unchecked")Node[] nt = (Node[])new Node[n];table = nt;//sc=0.75n,相当于扩容阈值sc = n - (n >>> 2);}} finally {//此时并没有通过CAS赋值 , 因为其他想要执行初始化的线程 , 发现sizeCtl=-1 , 就直接返回 , 从而确保任何情况 , 只会有一个线程执行初始化操作 。sizeCtl = sc;}}}//目标扩容size小于扩容阈值 , 或者容量超过最大限制时 , 不需要扩容else if (c


推荐阅读