ConcurrentHashMap静态公共方法分析

ConcurrentHashMap静态公共方法分析


分析ConcurrentHashMap中一些重要的公共方法,以便理解后面的源码。

1. spread 方法

spread()方法的作用是为了将hash值变得更加散列,从而减少hash冲突的产生。

翻译过来的方法注释:

“将哈希值较高的位传播到较低的位,并强制顶位为0。因为表使用2的次方掩码,所以仅在当前掩码以上的位上变化的散列集总是会碰撞。(已知的例子是在小表中保存连续整数的Float键集。)所以我们应用一个变换,向下传播更高位的影响。比特传播的速度、效用和质量之间需要权衡。因为许多常见的散列集已经合理分布(因此不会从扩散中受益),而且因为我们使用树来处理箱子中的大型碰撞集,所以我们只是以尽可能便宜的方式对一些移位的位进行XOR,以减少系统损失,以及合并由于表边界而永远不会在索引计算中使用的最高位的影响。”

/**
 * 计算Node节点hash值的算法:参数h为hash值
 * 例如:
 * h二进制为 --> 	 		 		 1100 0011 1010 0101 0001 1100 0001 1110
 * (h >>> 16) -->  					0000 0000 0000 0000 1100 0011 1010 0101 
 * (h ^ (h >>> 16)) -->				1100 0011 1010 0101 1101 1111 1011 1011
 * 注:(h ^ (h >>> 16)) 目的是让h的高16位也参与寻址计算,使得到的hash值更分散,减少hash冲突产生
 * ------------------------------------------------------------------------------
 * HASH_BITS -->					0111 1111 1111 1111 1111 1111 1111 1111
 * (h ^ (h >>> 16)) -->				1100 0011 1010 0101 1101 1111 1011 1011
 * (h ^ (h >>> 16)) & HASH_BITS --> 0100 0011 1010 0101 1101 1111 1011 1011
 * 注: (h ^ (h >>> 16))得到的结果再& HASH_BITS,目的是为了让得到的hash值结果始终是一个正数
 */
static final int spread(int h) {
    return (h ^ (h >>> 16)) & HASH_BITS;
}

2. tabAt 方法

tabAt()方法用于获取 Node 数组 table(桶)指定下标位置上的 Node 节点。

ASHIFT = 31 - Integer.numberOfLeadingZeros(scale),在静态代码块中ASHIFT被赋值,ConcurrentHashMap采用了位运算的方式来计算数组中元素的偏移地址而不是用乘法,这样会更加高效。

/**
 * 获取 Node 数组 table 中下标为 i 的 Node 节点
 * tab Node数组引用
 * i 数组下标
 */
@SuppressWarnings("unchecked")
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
    // getObjectVolatile() 是native方法,作用是从主存中获取线程共享变量
    // ((long)i << ASHIFT) + ABASE 通过运用位运算计算出目标节点的偏移地址
    return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
}

3. casTabAt 方法

/**
 * cas方式将table中的元素更新
 * tab Node数组table(哈希表)
 * i 要替换的元素的下标
 * c 期望节点值
 * v 新的节点值
 */
static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
                                    Node<K,V> c, Node<K,V> v) {
    // 调用Unsafe类的compareAndSwapObject()方法
    // 与tabAt方法一样计算出元素的偏移地址
    return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
}

4. setTabAt 方法

/**
 *
 * tab Node数组table(哈希表)
 * i table数组下标
 * 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);
}

5. resizeStamp 方法

/**
 * 返回用于调整大小为n的表大小的戳位。当被RESIZE_STAMP_SHIFT左移时必须为负。
 */
static final int resizeStamp(int n) {
    return Integer.numberOfLeadingZeros(n) | (1 << (RESIZE_STAMP_BITS - 1));
}

举例分析:

假设table需要冲16扩容到32时,numberOfLeadingZeros(16)的返回值就是27。

RESIZE_STAMP_BITS的值是16,经过(1 << (RESIZE_STAMP_BITS - 1)运算后再与27做与运算,就会得到一个扩容表示戳。

6. tableSizeFor 方法

tableSizeFor与HashMap中的tableSizeFor方法一样,传入一个数,通过位运算返回大于等于这个数的最小2次幂的数。

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: ConcurrentHashMap的size方法返回当前映射中键值对的数量。由于ConcurrentHashMap是线程安全的,因此在多个线程同时访问时,size方法可能会返回一个近似值,而不是精确的值。这是因为在多个线程同时进行插入、删除等操作时,size方法需要遍历整个映射,而这个过程中可能会有其他线程同时进行修改操作,导致size方法返回的值不是最新的。因此,ConcurrentHashMap的size方法适用于需要一个近似值的场景,如果需要精确的值,可以使用其他方法,如遍历整个映射并计数。 ### 回答2: ConcurrentHashMap的size方法是用来获取当前ConcurrentHashMap中键值对的数量的。ConcurrentHashMapJava中的一个线程安全的哈希表,它通过细粒度的锁机制来实现多线程并发访问的效率。 在ConcurrentHashMap中,不同的线程可以同时进行读取操作,而且读取操作不需要加锁,这大大提高了读取操作的效率。但是如果有多个线程同时进行写入操作,那么这些写入操作就需要加锁来保证线程安全。 在ConcurrentHashMap的实现中,为了同时满足读取的高效率和写入的线程安全,ConcurrentHashMap使用了分段锁机制。它将整个哈希表分成了多个段(Segment),每个段都有自己的锁。这样不同的线程在写入操作时,只需要获取对应段的锁,而不会影响其他段的读取操作。这就保证了ConcurrentHashMap在并发场景下的高性能。 ConcurrentHashMap的size方法的原理是遍历整个哈希表,统计各个段中的键值对数量,并将其累加得到最终的结果。由于ConcurrentHashMap的读取操作不需要加锁,所以在统计的过程中,其他线程可以进行并发的读取操作。 需要注意的是,由于ConcurrentHashMap的size方法是通过遍历整个哈希表来统计数量的,所以在极端的高并发场景下,size方法的返回结果可能不是完全准确的。不过在大多数情况下,ConcurrentHashMap的size方法还是可以提供一个相对准确的结果的。 ### 回答3: ConcurrentHashMapJava中的一个线程安全的哈希表实现,它继承自HashMap并通过使用锁分段技术来实现高并发性能。 ConcurrentHashMap的size()方法用于获取当前哈希表中键值对的数量。该方法通过遍历哈希表的所有段(每个段都是一个小的哈希表),获取每个段中的键值对数量,并将这些数量累加得到最终的结果。由于ConcurrentHashMap的并发性能较高,所以在计算size()的过程中,其他线程可以继续执行其他操作,不会被阻塞。 在计算size()的过程中,如果有其他线程对哈希表进行插入、删除或更新操作,那么在计算结果时可能会发生一些变化。为了保证计算结果的准确性,ConcurrentHashMap使用了一些特殊的技术来解决并发冲突,如使用volatile修饰符保证可见性、使用CAS(Compare and Swap)操作来保证原子性等。 需要注意的是,由于ConcurrentHashMap的size()方法是基于估计值计算得到的,所以在某些情况下,它可能无法返回准确的结果。比如,在计算size()的同时,有其他线程不断地进行插入或删除操作,那么返回的结果可能比实际的键值对数量要大或小一些。如果对结果的准确性有较高的要求,可以考虑使用其他方式来获取准确的数量,例如迭代哈希表并计算实际存在的键值对数量。 总之,ConcurrentHashMap的size()方法是用于获取线程安全哈希表中键值对数量的方法,它通过分段的方式来保证高并发性能,并通过一些特殊的技术来解决并发冲突。但是需要注意的是,由于size()方法是基于估计值计算得到的,所以结果可能不是完全准确的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

神烦狗闯入了你的博客

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

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

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

打赏作者

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

抵扣说明:

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

余额充值