【Java并发】- ConcurrentHashMap原理解析(JDK1.8)

ConcurrentHashMap概述

在JDK1.8中ConcurrentHashMap的实现方式或者说实现思想相比之前的版本已经相差了很大了,在JDK1.8之前ConcurrentHashMap是采用的是Segment(分段锁)的方式,而JDK1.8中的ConcurrentHashMap则摒弃了原来的做法,同时沿用了JDK1.8中的HashMap的数组+链表+红黑树的方式,同时大量的使用了java并发包作者非常多用的Unsafe里面的方法,也就是利用CAS的思想来实现,虽然已网上已经有了很多优秀的分析,但是为了加深印象还是自己写一下。

参考JDK1.8中的HashMap原理

数据构成

ConcurrentHashMap与HashMap的内部数据结构与HashMap的思想基本一致,都是使用的数组+链表+红黑树的,但是相比之下ConcurrentHashMap也有一些改动,比如ConcurrentHashMap的树结构并不是像HashMap那样直接链上TreeNode节点而是在内部定义了一个TreeBin的类来作为容器去包含了这些树节点,也就是说实际存放在数组上的对象是TreeBin而不是TreeNode,同时TreeNode也承担了红黑树的构造的工作。

并发

ConcurrentHashMap的数据的插入与删除等的基本流程和HashMap基本一致,在线程安全方面ConcurrentHashMap使用了CAS方式加上synchronized关键字而synchronizez使用的锁则是当前数组中需要加入的位置对应的数组元素,也就是“动哪儿锁哪儿”。所以并发上面性能也很好。

在HashMap中有一个很头疼的resize扩容很慢的问题也得到了解决,ConcurrentHashMap允许多个线程并发的去进行resize操作,这样大大的提高了效率。


源码解析

由于许多内容是和HashMap相同的,说明的会适当的简略一些。

重要的属性

//多线程扩容的时候单个线程负责的table中的元素个数
private static final int MIN_TRANSFER_STRIDE = 16;

private static int RESIZE_STAMP_BITS = 16;

private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;

private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;

static final int MOVED     = -1; // hash for forwarding nodes ForwardNode的hash值
static final int TREEBIN   = -2; // hash for roots of trees //TreeBin节点的的hash值
static final int RESERVED  = -3; // hash for transient reservations //ReservationNode节点的的hash值
static final int HASH_BITS = 0x7fffffff; // usable bits of normal node hash //用于计算hash值

//获取可用的CPU数
static final int NCPU = Runtime.getRuntime().availableProcessors();
//数组
transient volatile Node<K,V>[] table;
//只有在进行扩容的时候使用
private transient volatile Node<K,V>[] nextTable;
/*  当是负数的时候表示正在初始化或者resize
*   当是-1的时候表示正在初始化
*   -N 表示有N-1个线程正在进行resize
*/
private transient volatile int sizeCtl;
//用于多线程一起扩容的时候记录位置
private transient volatile int transferIndex;

其中的sizeCtl变量的出镜率很高。用于控制并发resize的时候的线程。


节点

Node节点
static class Node<K,V> implements Map.Entry<K,V> {
     final int hash;
     final K key;
     //这里与HashMap不同的是val和next被定义为了volatile
     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; }
     //不允许setValue(与HashMap不同)
     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)));
     }

     //定义了find方法用于辅助map.get()方法
     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;
     }
}

这里与HashMap不同的是使用volatile标识了val和next,是的val和next的读/写具备了原子性,可以配合CAS一起使用。

TreeNode

TreeNode和HashMap基本差不多。

TreeBin
static final class TreeBin<K,V> extends Node<K,V> {
   
    TreeNode<K,V> root;
    volatile TreeNode<K,V> first;
    volatile Thread waiter;
    volatile int lockState;
    // values for lockState
    static final int WRITER = 1; // set while holding write lock
    static final int WAITER = 2; // set when waiting for write lock
    static final int READER = 4; // increment value for setting read lock

    //构造红黑树
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值