ConcurrentHashMap底层简单讲解

前言

HashMap在多线程情况下,在put的时候,插入的元素超过了容量(由负载因子决定)的范围就会触发扩容操作,就是rehash,这个会重新将原数组的内容重新hash到新的扩容数组中,在多线程的环境下,存在同时其他的元素也在进行put操作,如果hash值相同,可能出现同时在同一数组下用链表表示,造成闭环,导致在get时会出现死循环,所以HashMap是线程不安全的。
HashTable,它是线程安全的,它在所有涉及到多线程操作的都加上了synchronized关键字来锁住整个table,这就意味着所有的线程都在竞争一把锁,在多线程的环境下,它是安全的,但是无疑是效率低下的。
不加锁不安全,加锁效率低,所以既然不能全锁(HashTable)又不能不锁(HashMap),所以就搞个部分锁,只锁部分,用到哪部分就锁哪部分。
ConcurrentHashMap的数据结构(数组+链表+红黑树),桶中的结构可能是链表,也可能是红黑树,红黑树是为了提高查找效率。

  // node数组最大容量:2^30=1073741824  

  private  static  final  int  MAXIMUM_CAPACITY =  1  <<  30    ;  

  // 默认初始值,必须是2的幂数  

  private  static  final  int  DEFAULT_CAPACITY =  16    ;  

  //数组可能最大值,需要与toArray()相关方法关联  

  static  final  int  MAX_ARRAY_SIZE = Integer.MAX_VALUE -  8    ;  

  //并发级别,遗留下来的,为兼容以前的版本  

  private  static  final  int  DEFAULT_CONCURRENCY_LEVEL =  16    ;  

  // 负载因子  

  private  static  final  float  LOAD_FACTOR =  0    .75f;  

  // 链表转红黑树阀值,> 8 链表转换为红黑树  

  static  final  int  TREEIFY_THRESHOLD =  8    ;  

  //树转链表阀值,小于等于6(tranfer时,lc、hc=0两个计数器分别++记录原bin、新binTreeNode数量,<=UNTREEIFY_THRESHOLD 则untreeify(lo))  

  static  final  int  UNTREEIFY_THRESHOLD =  6    ;  

  static  final  int  MIN_TREEIFY_CAPACITY =  64    ;  

  private  static  final  int  MIN_TRANSFER_STRIDE =  16    ;  

  private  static  int  RESIZE_STAMP_BITS =  16    ;  

  // 2^15-1,help resize的最大线程数  

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

  // 32-16=16,sizeCtl中记录size大小的偏移量  

  private  static  final  int  RESIZE_STAMP_SHIFT =  32  - RESIZE_STAMP_BITS;  

  // forwarding nodes的hash值  

  static  final  int  MOVED     = -    1    ;  

  // 树根节点的hash值  

  static  final  int  TREEBIN   = -    2    ;  

  // ReservationNode的hash值  

  static  final  int  RESERVED  = -    3    ;  

  // 可用处理器数量  

  static  final  int  NCPU = Runtime.getRuntime().availableProcessors();  

  //存放node的数组  

  transient  volatile  Node<K,V>[] table;  

  /*控制标识符,用来控制table的初始化和扩容的操作,不同的值有不同的含义  

  *当为负数时:-    1    代表正在初始化,-N代表有N-    1    个线程正在 进行扩容  

  *当为    0    时:代表当时的table还没有被初始化  

  *当为正数时:表示初始化或者下一次进行扩容的大小  
*/

  private  transient  volatile  int  sizeCtl;  


// 初始化容器的方法
private final Node<K, V>[] initTable() {
	Node<K, V>[] tab;
	int sc;
	// 如果容器为空或者容器大小为空 则开始循环
	while ((tab = table) == null || tab.length == 0) {
		// 如果目前需要扩容的大小是小于0的,则让出当前扩容线程的执行过程
		// 因为必然有其他线程在尝试扩容
		if ((sc = sizeCtl) < 0)
			Thread.yield(); // lost initialization race; just spin
		// 如果sc>=0 则去执行一次CAS过程,将本对象中的SIZECTL值替换成 -1, 原来的比较用的值
		// 是sc
		else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
			try {
				// 如果tab是空,或者没有长度
				if ((tab = table) == null || tab.length == 0) {
					// 刚才已经替换过sc了,这个时候的sc应该是-1
					int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
					// 所以第一次初始化的时候,容量是16
					@SuppressWarnings("unchecked")
					// 创建一个容量16的数组
					Node<K, V>[] nt = (Node<K, V>[]) new Node<?, ?>[n];
					table = tab = nt;
					// 重置sc为12;
					sc = n - (n >>> 2);
				}
			} finally {
				// 重置CTL为12
				sizeCtl = sc;
			}
			break;
		}
	}
	return tab;
}

Node是ConcurrentHashMap存储结构的基本单元,继承于HashMap中的Entry,用于存储数据,Node数据结构很简单,从上可知,就是一个链表,但是只允许对数据进行查找,不允许进行修改

  static  class  Node<K,V>  implements  Map.Entry<K,V> {  

  //链表的数据结构  

  final  int  hash;  

  final  K key;  

  //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; }  

  //不允许更新value   

  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)));  

  }  

  //用于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    ;  

  }  

  }  

TreeNode继承与Node,但是数据结构换成了二叉树结构,它是红黑树的数据的存储结构,用于红黑树中存储数据,当链表的节点数大于8时会转换成红黑树的结构,他就是通过TreeNode作为存储结构代替Node来转换成黑红树

  static  final  class  TreeNode<K,V>  extends  Node<K,V> {  

  //树形结构的属性定义  

  TreeNode<K,V> parent;  // red-black tree links  

  TreeNode<K,V> left;  

  TreeNode<K,V> right;  

  TreeNode<K,V> prev;  // needed to unlink next upon deletion  

  boolean  red;  //标志红黑树的红节点  

  TreeNode(    int  hash, K key, V val, Node<K,V> next,  

  TreeNode<K,V> parent) {  

  super    (hash, key, val, next);  

  this    .parent = parent;  

  }  

  Node<K,V> find(    int  h, Object k) {  

  return  findTreeNode(h, k,  null    );  

  }  

  //根据key查找 从根节点开始找出相应的TreeNode,  

  final  TreeNode<K,V> findTreeNode(    int  h, Object k, Class<?> kc) {  

  if  (k !=  null    ) {  

  TreeNode<K,V> p =  this    ;  

  do      {  

  int  ph, dir; K pk; TreeNode<K,V> q;  

  TreeNode<K,V> pl = p.left, pr = p.right;  

  if  ((ph = p.hash) > h)  

  p = pl;  

  else  if  (ph < h)  

  p = pr;  

  else  if  ((pk = p.key) == k || (pk !=  null  && k.equals(pk)))  

  return  p;  

  else  if  (pl ==  null    )  

  p = pr;  

  else  if  (pr ==  null    )  

  p = pl;  

  else  if  ((kc !=  null  ||  

  (kc = comparableClassFor(k)) !=  null    ) &&  

  (dir = compareComparables(kc, k, pk)) !=  0    )  

  p = (dir <  0    ) ? pl : pr;  

  else  if  ((q = pr.findTreeNode(h, k, kc)) !=  null    )  

  return  q;  

  else  

  p = pl;  

  }  while  (p !=  null    );  

  }  

  return  null    ;  

  }  

  }  

TreeBin从字面含义中可以理解为存储树形结构的容器,而树形结构就是指TreeNode,所以TreeBin就是封装TreeNode的容器,它提供转换黑红树的一些条件和锁的控制

  static  final  class  TreeBin<K,V>  extends  Node<K,V> {  

  //指向TreeNode列表和根节点  

  TreeNode<K,V> root;  

  volatile  TreeNode<K,V> first;  

  volatile  Thread waiter;  

  volatile  int  lockState;  

  // 读写锁状态  

  static  final  int  WRITER =  1    ;  // 获取写锁的状态  

  static  final  int  WAITER =  2    ;  // 等待写锁的状态  

  static  final  int  READER =  4    ;  // 增加数据时读锁的状态  

  /**  

  * 初始化红黑树  

  */  

  TreeBin(TreeNode<K,V> b) {  

  super    (TREEBIN,  null    ,  null    ,  null    );  

  this    .first = b;  

  TreeNode<K,V> r =  null    ;  

  for  (TreeNode<K,V> x = b, next; x !=  null    ; x = next) {  

  next = (TreeNode<K,V>)x.next;  

  x.left = x.right =  null    ;  

  if  (r ==  null    ) {  

  x.parent =  null    ;  

  x.red =  false    ;  

  r = x;  

  }  

  else  {  

  K k = x.key;  

  int  h = x.hash;  

  Class<?> kc =  null    ;  

  for  (TreeNode<K,V> p = r;;) {  

  int  dir, ph;  

  K pk = p.key;  

  if  ((ph = p.hash) > h)  

  dir = -    1    ;  

  else  if  (ph < h)  

  dir =  1    ;  

  else  if  ((kc ==  null  &&  

  (kc = comparableClassFor(k)) ==  null    ) ||  

  (dir = compareComparables(kc, k, pk)) ==  0    )  

  dir = tieBreakOrder(k, pk);  

  TreeNode<K,V> xp = p;  

  if  ((p = (dir <=  0    ) ? p.left : p.right) ==  null    ) {  

  x.parent = xp;  

  if  (dir <=  0    )  

  xp.left = x;  

  else  

  xp.right = x;  

  r = balanceInsertion(r, x);  

  break    ;  

  }  

  }  

  }  

  }  

  this    .root = r;  

  assert  checkInvariants(root);  

  }  

  ......  

  }  

put方法

如果没有初始化就先调用initTable()方法来进行初始化过程
如果没有hash冲突就直接CAS插入
如果还在进行扩容操作就先进行扩容
如果存在hash冲突,就加锁来保证线程安全,这里有两种情况,一种是链表形式就直接遍历到尾端插入,一种是红黑树就按照红黑树结构插入,
最后一个如果Hash冲突时会形成Node链表,在链表长度超过8,Node数组超过64时会将链表结构转换为红黑树的结构,break再一次进入循环
如果添加成功就调用addCount()方法统计size,并且检查是否需要扩容

  public  V put(K key, V value) {  

  return  putVal(key, value,  false    );  

  }  

  /** Implementation for put and putIfAbsent */  

  final  V putVal(K key, V value,  boolean  onlyIfAbsent) {  

  if  (key ==  null  || value ==  null    )  throw  new  NullPointerException();  

  int  hash = spread(key.hashCode());  //两次hash,减少hash冲突,可以均匀分布  

  int  binCount =  0    ;  

  for  (Node<K,V>[] tab = table;;) {  //对这个table进行迭代  

  Node<K,V> f;  int  n, i, fh;  

  //这里就是上面构造方法没有进行初始化,在这里进行判断,为null就调用initTable进行初始化,属于懒汉模式初始化  

  if  (tab ==  null  || (n = tab.length) ==  0    )  

  tab = initTable();  

  else  if  ((f = tabAt(tab, i = (n -  1    ) & hash)) ==  null    ) {    //如果i位置没有数据,就直接无锁插入  

  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    ;  

  //如果以上条件都不满足,那就要进行加锁操作,也就是存在hash冲突,锁住链表或者红黑树的头结点  

  synchronized  (f) {  

  if  (tabAt(tab, i) == f) {  

  if  (fh >=  0    ) {  //表示该节点是链表结构  

  binCount =  1    ;  

  for  (Node<K,V> e = f;; ++binCount) {  

  K ek;  

  //这里涉及到相同的key进行put就会覆盖原先的value  

  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    ) {  //如果链表的长度大于8时就会进行红黑树的转换  

  if  (binCount >= TREEIFY_THRESHOLD)  

  treeifyBin(tab, i);  

  if  (oldVal !=  null    )  

  return  oldVal;  

  break    ;  

  }  

  }  

  }  

  addCount(1L, binCount);    //统计size,并且检查是否需要扩容  

  return  null    ;  

  }  

put函数底层调用了putVal进行数据的插入,对于putVal函数的流程大体如下。
① 判断存储的key、value是否为空,若为空,则抛出异常,否则,进入步骤②
② 计算key的hash值,随后进入无限循环,该无限循环可以确保成功插入数据,若table表为空或者长度为0,则初始化table表,否则,进入步骤③
③ 根据key的hash值取出table表中的结点元素,若取出的结点为空(该桶为空),则使用CAS将key、value、hash值生成的结点放入桶中。否则,进入步骤④
④ 若该结点的的hash值为MOVED,则对该桶中的结点进行转移,否则,进入步骤⑤
⑤ 对桶中的第一个结点(即table表中的结点)进行加锁,对该桶进行遍历,桶中的结点的hash值与key值与给定的hash值和key值相等,则根据标识选择是否进行更新操作(用给定的value值)

initTable()方法
对于table的大小,会根据sizeCtl的值进行设置,如果没有设置szieCtl的值,那么默认生成的table大小为16,否则,会根据sizeCtl的大小设置table大小。


  /**  

  * Initializes table, using the size recorded in sizeCtl.  

  */  

  private  final  Node<K,V>[] initTable() {  

  Node<K,V>[] tab;  int  sc;  

  while  ((tab = table) ==  null  || tab.length ==  0    ) {    //空的table才能进入初始化操作  

  if  ((sc = sizeCtl) <  0    )  //sizeCtl<0表示其他线程已经在初始化了或者扩容了,挂起当前线程  

  Thread.yield();  // lost initialization race; just spin  

  else  if  (U.compareAndSwapInt(    this    , SIZECTL, sc, -    1    )) {    //CAS操作SIZECTL为-1,表示初始化状态  

  try  {  

  if  ((tab = table) ==  null  || tab.length ==  0    ) {  

  int  n = (sc >  0    ) ? sc : DEFAULT_CAPACITY;  

  @SuppressWarnings    (    "unchecked"    )  

  Node<K,V>[] nt = (Node<K,V>[])    new  Node<?,?>[n];    //初始化  

  table = tab = nt;  

  sc = n - (n >>>  2    );    //记录下次扩容的大小  

  }  

  }  finally  {  

  sizeCtl = sc;  

  }  

  break    ;  

  }  

  }  

  return  tab;  

  }  


tabAt方法

static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
        return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
    }

此函数返回table数组中下标为i的结点,可以看到是通过Unsafe对象通过反射获取的,getObjectVolatile的第二项参数为下标为i的偏移地址。
casTabAt方法

static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
                                        Node<K,V> c, Node<K,V> v) {
        return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
    }

此函数用于比较table数组下标为i的结点是否为c,若为c,则用v交换操作。否则,不进行交换操作。
helpTransfer()方法

  /**  

  *帮助从旧的table的元素复制到新的table中  

  */  

  final  Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {  

  Node<K,V>[] nextTab;  int  sc;  

  if  (tab !=  null  && (f  instanceof  ForwardingNode) &&  

  (nextTab = ((ForwardingNode<K,V>)f).nextTable) !=  null    ) {  //新的table nextTba已经存在前提下才能帮助扩容  

  int  rs = resizeStamp(tab.length);  

  while  (nextTab == nextTable && table == tab &&  

  (sc = sizeCtl) <  0    ) {  

  if  ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs +  1  ||  

  sc == rs + MAX_RESIZERS || transferIndex <=  0    )  

  break    ;  

  if  (U.compareAndSwapInt(    this    , SIZECTL, sc, sc +  1    )) {  

  transfer(tab, nextTab);    //调用扩容方法  

  break    ;  

  }  

  }  

  return  nextTab;  

  }  

  return  table;  

  }  

扩容方法transfer()

  private  final  void  transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {  

  int  n = tab.length, stride;  

  // 每核处理的量小于16,则强制赋值16  

  if  ((stride = (NCPU >  1    ) ? (n >>>  3    ) / NCPU : n) < MIN_TRANSFER_STRIDE)  

  stride = MIN_TRANSFER_STRIDE;  // subdivide range  

  if  (nextTab ==  null    ) {  // initiating  

  try  {  

  @SuppressWarnings    (    "unchecked"    )  

  Node<K,V>[] nt = (Node<K,V>[])    new  Node<?,?>[n <<  1    ];  //构建一个nextTable对象,其容量为原来容量的两倍  

  nextTab = nt;  

  }  catch  (Throwable ex) {  // try to cope with OOME  

  sizeCtl = Integer.MAX_VALUE;  

  return    ;  

  }  

  nextTable = nextTab;  

  transferIndex = n;  

  }  

  int  nextn = nextTab.length;  

  // 连接点指针,用于标志位(fwd的hash值为-1,fwd.nextTable=nextTab)  

  ForwardingNode<K,V> fwd =  new  ForwardingNode<K,V>(nextTab);  

  // 当advance == true时,表明该节点已经处理过了  

  boolean  advance =  true    ;  

  boolean  finishing =  false    ;  // to ensure sweep before committing nextTab  

  for  (    int  i =  0    , bound =  0    ;;) {  

  Node<K,V> f;  int  fh;  

  // 控制 --i ,遍历原hash表中的节点  

  while  (advance) {  

  int  nextIndex, nextBound;  

  if  (--i >= bound || finishing)  

  advance =  false    ;  

  else  if  ((nextIndex = transferIndex) <=  0    ) {  

  i = -    1    ;  

  advance =  false    ;  

  }  

  // 用CAS计算得到的transferIndex  

  else  if  (U.compareAndSwapInt  

  (    this    , TRANSFERINDEX, nextIndex,  

  nextBound = (nextIndex > stride ?  

  nextIndex - stride :  0    ))) {  

  bound = nextBound;  

  i = nextIndex -  1    ;  

  advance =  false    ;  

  }  

  }  

  if  (i <  0  || i >= n || i + n >= nextn) {  

  int  sc;  

  // 已经完成所有节点复制了  

  if  (finishing) {  

  nextTable =  null    ;  

  table = nextTab;  // table 指向nextTable  

  sizeCtl = (n <<  1    ) - (n >>>  1    );  // sizeCtl阈值为原来的1.5倍  

  return    ;  // 跳出死循环,  

  }  

  // CAS 更扩容阈值,在这里面sizectl值减一,说明新加入一个线程参与到扩容操作  

  if  (U.compareAndSwapInt(    this    , SIZECTL, sc = sizeCtl, sc -  1    )) {  

  if  ((sc -  2    ) != resizeStamp(n) << RESIZE_STAMP_SHIFT)  

  return    ;  

  finishing = advance =  true    ;  

  i = n;  // recheck before commit  

  }  

  }  

  // 遍历的节点为null,则放入到ForwardingNode 指针节点  

  else  if  ((f = tabAt(tab, i)) ==  null    )  

  advance = casTabAt(tab, i,  null    , fwd);  

  // f.hash == -1 表示遍历到了ForwardingNode节点,意味着该节点已经处理过了  

  // 这里是控制并发扩容的核心  

  else  if  ((fh = f.hash) == MOVED)  

  advance =  true    ;  // already processed  

  else  {  

  // 节点加锁  

  synchronized  (f) {  

  // 节点复制工作  

  if  (tabAt(tab, i) == f) {  

  Node<K,V> ln, hn;  

  // fh >= 0 ,表示为链表节点  

  if  (fh >=  0    ) {  

  // 构造两个链表  一个是原链表  另一个是原链表的反序排列  

  int  runBit = fh & n;  

  Node<K,V> lastRun = f;  

  for  (Node<K,V> p = f.next; p !=  null    ; p = p.next) {  

  int  b = p.hash & n;  

  if  (b != runBit) {  

  runBit = b;  

  lastRun = p;  

  }  

  }  

  if  (runBit ==  0    ) {  

  ln = lastRun;  

  hn =  null    ;  

  }  

  else  {  

  hn = lastRun;  

  ln =  null    ;  

  }  

  for  (Node<K,V> p = f; p != lastRun; p = p.next) {  

  int  ph = p.hash; K pk = p.key; V pv = p.val;  

  if  ((ph & n) ==  0    )  

  ln =  new  Node<K,V>(ph, pk, pv, ln);  

  else  

  hn =  new  Node<K,V>(ph, pk, pv, hn);  

  }  

  // 在nextTable i 位置处插上链表  

  setTabAt(nextTab, i, ln);  

  // 在nextTable i + n 位置处插上链表  

  setTabAt(nextTab, i + n, hn);  

  // 在table i 位置处插上ForwardingNode 表示该节点已经处理过了  

  setTabAt(tab, i, fwd);  

  // advance = true 可以执行--i动作,遍历节点  

  advance =  true    ;  

  }  

  // 如果是TreeBin,则按照红黑树进行处理,处理逻辑与上面一致  

  else  if  (f  instanceof  TreeBin) {  

  TreeBin<K,V> t = (TreeBin<K,V>)f;  

  TreeNode<K,V> lo =  null    , loTail =  null    ;  

  TreeNode<K,V> hi =  null    , hiTail =  null    ;  

  int  lc =  0    , hc =  0    ;  

  for  (Node<K,V> e = t.first; e !=  null    ; e = e.next) {  

  int  h = e.hash;  

  TreeNode<K,V> p =  new  TreeNode<K,V>  

  (h, e.key, e.val,  null    ,  null    );  

  if  ((h & n) ==  0    ) {  

  if  ((p.prev = loTail) ==  null    )  

  lo = p;  

  else  

  loTail.next = p;  

  loTail = p;  

  ++lc;  

  }  

  else  {  

  if  ((p.prev = hiTail) ==  null    )  

  hi = p;  

  else  

  hiTail.next = p;  

  hiTail = p;  

  ++hc;  

  }  

  }  

  // 扩容后树节点个数若<=6,将树转链表  

  ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) :  

  (hc !=  0    ) ?  new  TreeBin<K,V>(lo) : t;  

  hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) :  

  (lc !=  0    ) ?  new  TreeBin<K,V>(hi) : t;  

  setTabAt(nextTab, i, ln);  

  setTabAt(nextTab, i + n, hn);  

  setTabAt(tab, i, fwd);  

  advance =  true    ;  

  }  

  }  

  }  

  }  

  }  

  }  

putTreeVal方法

final TreeNode<K,V> putTreeVal(int h, K k, V v) {
    Class<?> kc = null;
    boolean searched = false;
    for (TreeNode<K,V> p = root;;) {
        int dir, ph; K pk;
        if (p == null) {
            first = root = new TreeNode<K,V>(h, k, v, null, null);
            break;
        }
        else if ((ph = p.hash) > h)
            dir = -1;
        else if (ph < h)
            dir = 1;
        else if ((pk = p.key) == k || (pk != null && k.equals(pk)))
            return p;
        else if ((kc == null &&
                (kc = comparableClassFor(k)) == null) ||
                (dir = compareComparables(kc, k, pk)) == 0) {
            if (!searched) {
                TreeNode<K,V> q, ch;
                searched = true;
                if (((ch = p.left) != null &&
                        (q = ch.findTreeNode(h, k, kc)) != null) ||
                        ((ch = p.right) != null &&
                                (q = ch.findTreeNode(h, k, kc)) != null))
                    return q;
            }
            dir = tieBreakOrder(k, pk);
        }

        TreeNode<K,V> xp = p;
        if ((p = (dir <= 0) ? p.left : p.right) == null) {
            TreeNode<K,V> x, f = first;
            first = x = new TreeNode<K,V>(h, k, v, f, xp);
            if (f != null)
                f.prev = x;
            if (dir <= 0)
                xp.left = x;
            else
                xp.right = x;
            if (!xp.red)
                x.red = true;
            else {
                lockRoot();
                try {
                    root = balanceInsertion(root, x);
                } finally {
                    unlockRoot();
                }
            }
            break;
        }
    }
    assert checkInvariants(root);
    return null;
}

此函数用于将指定的hash、key、value值添加到红黑树中,若已经添加了,则返回null,否则返回该结点。
treeifyBin()方法进行链表转红黑树的过程

  private  final  void  treeifyBin(Node<K,V>[] tab,  int  index) {  

  Node<K,V> b;  int  n, sc;  

  if  (tab !=  null    ) {  

  //如果整个table的数量小于64,就扩容至原来的一倍,不转红黑树了  

  //因为这个阈值扩容可以减少hash冲突,不必要去转红黑树  

  if  ((n = tab.length) < MIN_TREEIFY_CAPACITY)  

  tryPresize(n <<  1    );  

  else  if  ((b = tabAt(tab, index)) !=  null  && b.hash >=  0    ) {  

  synchronized  (b) {  

  if  (tabAt(tab, index) == b) {  

  TreeNode<K,V> hd =  null    , tl =  null    ;  

  for  (Node<K,V> e = b; e !=  null    ; e = e.next) {  

  //封装成TreeNode  

  TreeNode<K,V> p =  

  new  TreeNode<K,V>(e.hash, e.key, e.val,  

  null    ,  null    );  

  if  ((p.prev = tl) ==  null    )  

  hd = p;  

  else  

  tl.next = p;  

  tl = p;  

  }  

  //通过TreeBin对象对TreeNode转换成红黑树  

  setTabAt(tab, index,  new  TreeBin<K,V>(hd));  

  }  

  }  

  }  

  }  

  }  

此函数用于将桶中的数据结构转化为红黑树,其中,值得注意的是,当table的长度未达到阈值时,会进行一次扩容操作,该操作会使得触发treeifyBin操作的某个桶中的所有元素进行一次重新分配,这样可以避免某个桶中的结点数量太大。
addCount方法

  private  final  void  addCount(    long  x,  int  check) {  

  CounterCell[] as;  long  b, s;  

  //更新baseCount,table的数量,counterCells表示元素个数的变化  

  if  ((as = counterCells) !=  null  ||  

  !U.compareAndSwapLong(    this    , BASECOUNT, b = baseCount, s = b + x)) {  

  CounterCell a;  long  v;  int  m;  

  boolean  uncontended =  true    ;  

  //如果多个线程都在执行,则CAS失败,执行fullAddCount,全部加入count  

  if  (as ==  null  || (m = as.length -  1    ) <  0  ||  

  (a = as[ThreadLocalRandom.getProbe() & m]) ==  null  ||  

  !(uncontended =  

  U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {  

  fullAddCount(x, uncontended);  

  return    ;  

  }  

  if  (check <=  1    )  

  return    ;  

  s = sumCount();  

  }  

  //check>=0表示需要进行扩容操作  

  if  (check >=  0    ) {  

  Node<K,V>[] tab, nt;  int  n, sc;  

  while  (s >= (    long    )(sc = sizeCtl) && (tab = table) !=  null  &&  

  (n = tab.length) < MAXIMUM_CAPACITY) {  

  int  rs = resizeStamp(n);  

  if  (sc <  0    ) {  

  if  ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs +  1  ||  

  sc == rs + MAX_RESIZERS || (nt = nextTable) ==  null  ||  

  transferIndex <=  0    )  

  break    ;  

  if  (U.compareAndSwapInt(    this    , SIZECTL, sc, sc +  1    ))  

  transfer(tab, nt);  

  }  

  //当前线程发起库哦哦让操作,nextTable=null  

  else  if  (U.compareAndSwapInt(    this    , SIZECTL, sc,  

  (rs << RESIZE_STAMP_SHIFT) +  2    ))  

  transfer(tab,  null    );  

  s = sumCount();  

  }  

  }  

  }  

此函数主要完成binCount的值加1的操作
get方法

public V get(Object key) {
    Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
    // 计算key的hash值
    int h = spread(key.hashCode()); 
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (e = tabAt(tab, (n - 1) & h)) != null) { // 表不为空并且表的长度大于0并且key所在的桶不为空
        if ((eh = e.hash) == h) { // 表中的元素的hash值与key的hash值相等
            if ((ek = e.key) == key || (ek != null && key.equals(ek))) // 键相等
                // 返回值
                return e.val;
        }
        else if (eh < 0) // 结点hash值小于0
            // 在桶(链表/红黑树)中查找
            return (p = e.find(h, key)) != null ? p.val : null;
        while ((e = e.next) != null) { // 对于结点hash值大于0的情况
            if (e.hash == h &&
                ((ek = e.key) == key || (ek != null && key.equals(ek))))
                return e.val;
        }
    }
    return null;
}

get函数根据key的hash值来计算在哪个桶中,再遍历桶,查找元素,若找到则返回该结点,否则,返回null。
replaceNode方法

final V replaceNode(Object key, V value, Object cv) {
    // 计算key的hash值
    int hash = spread(key.hashCode());
    for (Node<K,V>[] tab = table;;) { // 无限循环
        Node<K,V> f; int n, i, fh;
        if (tab == null || (n = tab.length) == 0 ||
            (f = tabAt(tab, i = (n - 1) & hash)) == null) // table表为空或者表长度为0或者key所对应的桶为空
            // 跳出循环
            break;
        else if ((fh = f.hash) == MOVED) // 桶中第一个结点的hash值为MOVED
            // 转移
            tab = helpTransfer(tab, f);
        else {
            V oldVal = null;
            boolean validated = false;
            synchronized (f) { // 加锁同步
                if (tabAt(tab, i) == f) { // 桶中的第一个结点没有发生变化
                    if (fh >= 0) { // 结点hash值大于0
                        validated = true;
                        for (Node<K,V> e = f, pred = null;;) { // 无限循环
                            K ek;
                            if (e.hash == hash &&
                                ((ek = e.key) == key ||
                                 (ek != null && key.equals(ek)))) { // 结点的hash值与指定的hash值相等,并且key也相等
                                V ev = e.val;
                                if (cv == null || cv == ev ||
                                    (ev != null && cv.equals(ev))) { // cv为空或者与结点value相等或者不为空并且相等
                                    // 保存该结点的val值
                                    oldVal = ev;
                                    if (value != null) // value为null
                                        // 设置结点value值
                                        e.val = value;
                                    else if (pred != null) // 前驱不为空
                                        // 前驱的后继为e的后继,即删除了e结点
                                        pred.next = e.next;
                                    else
                                        // 设置table表中下标为index的值为e.next
                                        setTabAt(tab, i, e.next);
                                }
                                break;
                            }
                            pred = e;
                            if ((e = e.next) == null)
                                break;
                        }
                    }
                    else if (f instanceof TreeBin) { // 为红黑树结点类型
                        validated = true;
                        // 类型转化
                        TreeBin<K,V> t = (TreeBin<K,V>)f;
                        TreeNode<K,V> r, p;
                        if ((r = t.root) != null &&
                            (p = r.findTreeNode(hash, key, null)) != null) { // 根节点不为空并且存在与指定hash和key相等的结点
                            // 保存p结点的value
                            V pv = p.val;
                            if (cv == null || cv == pv ||
                                (pv != null && cv.equals(pv))) { // cv为空或者与结点value相等或者不为空并且相等
                                oldVal = pv;
                                if (value != null) 
                                    p.val = value;
                                else if (t.removeTreeNode(p)) // 移除p结点
                                    setTabAt(tab, i, untreeify(t.first));
                            }
                        }
                    }
                }
            }
            if (validated) {
                if (oldVal != null) {
                    if (value == null)
                        // baseCount值减一
                        addCount(-1L, -1);
                    return oldVal;
                }
                break;
            }
        }
    }
    return null;
}

此函数对remove函数提供支持,remove函数底层是调用的replaceNode函数实现结点的删除。

总结

ConcurrentHashMap内部大量采用CAS操作,CAS是compare and swap的缩写,即我们所说的比较交换。cas是一种基于锁的操作,而且是乐观锁。在java中锁分为乐观锁和悲观锁。悲观锁是将资源锁住,等一个之前获得锁的线程释放锁之后,下一个线程才可以访问。而乐观锁采取了一种宽泛的态度,通过某种方式不加锁来处理资源,比如通过给记录加version来获取数据,性能较悲观锁有很大的提高。
Hashtable低效主要是因为所有访问Hashtable的线程都争夺一把锁。如果容器有很多把锁,每一把锁控制容器中的一部分数据,那么当多个线程访问容器里的不同部分的数据时,线程之前就不会存在锁的竞争,这样就可以有效的提高并发的访问效率。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值