ConcurrentHashmap size(1.7 1.8)

13203 ワード

ConcurrentHashmap size 1.7メソッド実装


よく知られているように、concurrenthashmapには多くの歌segmentsがあり、まずsegmentsを巡って各segmentのcountを加えてconcurrenthashMap全体のsizeとする.併発していなければ当然であるが、これはマルチスレッドであり、前足統計が終わって後足が変化した場合、これは正確ではなく、ソースコードに導入され、modCountと2回の比較でsizeの確認が実現される.具体的な手順は次のとおりです.
1.segments配列を1回目に巡回し、各segemntのcountを合計とし、期間中に各segmentのmodCountをsumと加算して結果が修正されたか否かの判断根拠とする.ここでmodCountについてお話ししますが、これはsegmentに何らかの操作がある場合にインクリメンタル操作が行われ、Segmentの要素の数に影響を与える操作の回数を表しています.この値は増加しても減らない!!!増加しても減少しないことが重要であり、1つのsegment+1は現れず、modcount+1をもたらし、もう1つのsegment-1、すなわちmodcount-1は、すべてを統計するときにmodcountが変化しない.
2.sizeオペレーションとは、すべてのSegmentsを2回巡回し、SegmentのmodCount値を記録するたびに2回のmodCountを比較し、同じであれば期間に書き込みが発生していないことを示し、元の巡回の結果を返し、異なる場合はこのプロセスをもう一度繰り返し、異なる場合はすべてのSegmentをロックして、1つずつ巡回する必要がある.
3.2回統計されたmodCountが一致していないと判断された場合は、上記のように、すべてのsegmentロックを再有効にしてcountの取得と統計を行い、その間に各segementがロックされ、他の操作ができず、統計されたcountは自然に正確である.
ロックをかけずに判断する理由は明らかで、size操作でこんなに多くのロックを取得することを望んでいない.ロックを取得することは資源を占有するだけでなく、他のスレッドのConcurrentHashの使用にも影響し、同時の場合のプログラム実行の効率にも影響を与えるからだ.鍵を使うには慎重に!

ConcurrentHashmap size 1.8メソッド実装


1.8でvolatileタイプの変数baseCountを使用して要素の個数を記録し、新しいデータを挿入したり削除したりすると、addCount()メソッドでbaseCountが更新され、以下のように実現される.
if ((as = counterCells) != null ||
    !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
    CounterCell a; long v; int m;
    boolean uncontended = true;
    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();
}

1、初期化時counterCellsは空で、同時量が高い場合、CASを同時に実行してbaseCount値を修正する2つのスレッドがある場合、失敗したスレッドは方法体の論理を実行し続け、CounterCellを使用して要素の個数の変化を記録する.
2、CounterCell配列counterCellsが空の場合、fullAddCount()メソッドを呼び出して初期化し、対応するレコード数を挿入し、CASでcellsBusyフィールドを設定し、成功したスレッドを設定してこそCounterCell配列を初期化することができ、以下のように実現する.
else if (cellsBusy == 0 && counterCells == as &&
         U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
    boolean init = false;
    try {                           // Initialize table
        if (counterCells == as) {
            CounterCell[] rs = new CounterCell[2];
            rs[h & 1] = new CounterCell(x);
            counterCells = rs;
            init = true;
        }
    } finally {
        cellsBusy = 0;
    }
    if (init)
        break;
}

3、CASでcellsBusyフィールドの設定に失敗した場合は、CASでbaseCountフィールドを変更し続け、baseCountフィールドの変更に成功した場合はループを終了し、そうでなければCounterCellオブジェクトをループ挿入し続ける.
else if (U.compareAndSwapLong(this, BASECOUNT, v = baseCount, v + x))
    break;
 1.8 size 1.7 , baseCount , CounterCell , :

public int size() {
    long n = sumCount();
    return ((n < 0L) ? 0 :
            (n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE :
            (int)n);
}
 
final long sumCount() {
    CounterCell[] as = counterCells; CounterCell a;
    long sum = baseCount;
    if (as != null) {
        for (int i = 0; i < as.length; ++i) {
            if ((a = as[i]) != null)
                sum += a.value;
        }
    }
    return sum;
}

baseCountとCounterCell配列の数を加算することで、要素の合計個数を得ることができます.