【Java】【HashMap】JDK1.7中ConcurrentHashMap是如何解决调用size时的一致性问题呢?

  • 再调用【size()】时,会出现什么样的一致性问题呢?

1》在没有put的情况下,调用concurrentHashMap的size()方法,可以直接获取到表的长度

2》在线程A调用size()方法的同时,线程B试图PUT了一个数据,那么线程A如何保证size()结果一致性呢?

  • 当然,JDK设计人员一定也想到了这个问题,官方源码如下:
public int size() {
    // Try a few times to get accurate count. On failure due to
   // continuous async changes in table, resort to locking.
   final Segment<K,V>[] segments = this.segments;
    int size;
    boolean overflow; // true if size overflows 32 bits
    long sum;         // sum of modCounts
    long last = 0L;   // previous sum
    int retries = -1; // first iteration isn't retry
    try {
        for (;;) {
            if (retries++ == RETRIES_BEFORE_LOCK) {//5.判断尝试次数是否尝试最大值
                for (int j = 0; j < segments.length; ++j)//6.对所有segment加锁,然后重新统计
                    ensureSegment(j).lock(); // force creation
            }
            sum = 0L;
            size = 0;
            overflow = false;
            for (int j = 0; j < segments.length; ++j) {//1.遍历所有Segment
                Segment<K,V> seg = segmentAt(segments, j);
                if (seg != null) {
                    sum += seg.modCount; //3.把segment的修改次数累加起来
                    int c = seg.count; //2.把segment的元素数量累加起来
                    if (c < 0 || (size += c) < 0)
                        overflow = true;
                }
            }
            if (sum == last)//4.判断segment总修改书是否大于上一次修改数
                break;
            last = sum;
        }
    } finally {
        if (retries > RETRIES_BEFORE_LOCK) {
            for (int j = 0; j < segments.length; ++j)
                segmentAt(segments, j).unlock();//7.释放锁,统计结束
        }
    }
    return overflow ? Integer.MAX_VALUE : size;
}
  • 单看源码一定很抽象,我们简述一下源码流程吧:

1》遍历所有的Segment。

2》把Segment的元素数量累加起来。

3》把Segment的修改次数累加起来。

4》判断所有Segment的总修改次数是否大于上一次的总修改次数。如果大于,说明统计过程中有修改,重新统计,尝试次数+1;如果不是。说明没有修改,统计结束。

5》如果尝试次数超过尝试最大值,则对每一个Segment加锁,再重新统计。

6》由于已经加锁,上一次修改次数和本次修改次数一定相等;然后释放锁,统计结束

  • 总结一下设计思想:

整体流程成可以抽象理解成是由乐观锁转向悲观锁的一个过程。

先不锁住所有Segment,而是乐观的假设调用size()过程不会有修改。

当尝试一定次数依然有修改的话,才会悲观的将所有Segment锁住,从而保证强一致性。

原文地址:https://www.cnblogs.com/boluopabo/p/13031509.html