ConcurrentHashMap源码解析2.内部简单方法

1.spread(int h)方法

ConcurrentHashMap中key的hash值的计算方式,是通过spread() 方法计算得到的。

static final int spread(int h) {
	    /*
	     *  其中h是key的hashCode()得到的。
	     *  h ^ (h >>> 16)就是让高16位与低16位进行异或,使得计算出来的哈希值更加散列。
	     *  
	     *  然后 & HASH_BITS(二进制是31个1)就是为了让最终得到的哈希值始终是一个正数。
	     */	
        return (h ^ (h >>> 16)) & HASH_BITS;
}

2.tabAt方法

  /*
   * 方法作用: 就是通过UnSafe的本地方法直接操作内存获取table中指定位置的元素,直接操作内存,效率高。
   *
   * @return 获取哈希表中指定位置的Node
   * @param tab 表示哈希表
   * @param i 需要获取元素的索引
   */
  static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
         
        /*
         *  U : UnSafe对象实例
         *  ABASE : 数组对象头大小
         *
         *  (i << ASHIFT + ABASE) = (ABASE + Scale(单个Node的内存大小) * i) 就是   		 
         *  得到当前Node[]数组下标为i的节点对象的偏移地址
         *  
         *  然后,通过getgetObjectVolatile()直接获取内存中指定位置的Node。
         */
        return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
    }

3.casTabAt

	/*
	 *  作用: 底层通过UnSafe的本地方法,通过CAS的方式向哈希表中的指定位置设置节点,直接操作内存,效率更高
	 *
     *  @param tab: 表示Node[]数组
     *  @param i  表示数组下标
     *  @param c  表示期望节点值
     *  @param v  表示要设置的节点值
     *  
	 */ 	
static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
                                        Node<K,V> c, Node<K,V> v) {
    	/*
    	 *    native方法
    	 * U: UnSafe对象的实例
    	 * ((long)i << ASHIFT) + ABASE : 内存偏移地址
    	 * c: 期望节点值
    	 * v: 要设置的节点值
    	 */
    	
        return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
    }

4.setTabAt方法

    /*
     *  根据下标i,设置table中对应位置的节点。
     *  
     *  @param tab 表示table数组
     *  @param i 要设置节点索引位置
     *  @param v 要设置的节点
     */
    static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
        
        /*
         *  通过调用UnSafe的本地方法putObjectVolatile,直接操作内存,为当前内存空间设置值 (具体参数就不分析了,跟上面一样)
         */
        U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
    }

通过上面的三个方法可以得到,ConcurrentHashMap无论是获取指定的Node还是设置Node都是直接通过UnSafe操作的内存,效率更高。

5.resizeStamp方法

//table数组扩容时,计算出一个扩容标识戳,当需要并发扩容时,当前线程必须拿到扩容标识戳才能参与扩容
static final int resizeStamp(int n) {
      /*
       *  RESIZE_STAMP_BITS :固定值16,与扩容相关,计算扩容时会根据该属性生成一个扩容标识戳
       *  
       *  当我们需要table容量从16扩容到32时,Integer.numberOfLeadingZeros(16)会得到
       *  27,怎样得到的?
       *  
       *  numberOfLeadingZeros(int n)方法,会根据传入的n,返回当前数值转换为二进制后,
       *  从高位开始统计,统计有多少个连续的0
       *  16的二进制表示为10000,Integer占32位,10000有5位,所以连续最长的0有27位
       * 
       *  1 << 15 = 32768
       *  
       *  两者进行|操作得到扩容标识戳
       */
     return Integer.numberOfLeadingZeros(n) | (1 << (RESIZE_STAMP_BITS - 1));  }

6.tableSizeFor方法

	 /*
 	  * 传入一个int类型的变量c,返回一个大于或等于c的 最小的2的幂。
      * 比如传入 16 返回16
	  * 传入 17 返回 32
	  */
private static final int tableSizeFor(int c) {
    int n = c - 1;
    n |= n >>> 1;
    n |= n >>> 2;
    n |= n >>> 4;
    n |= n >>> 8;
    n |= n >>> 16;
    return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shstart7

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值