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配列の数を加算することで、要素の合計個数を得ることができます.
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配列の数を加算することで、要素の合計個数を得ることができます.