ソースコードに基づいて、その基本的な実装プロセスを分析してみましょう。
複数のスレッドが、きめ細かい同期制御ではなく、統計収集などの目的で使用される共通の合計を更新する場合、通常、このクラスは AtomicLong よりも推奨されます。ただし、競合が多い場合、スペース消費量が増える代わりに、このクラスの予想スループットは大幅に高くなります。
上記の段落は、LongAdder
ソース コード コメントからのものです。その一部を大まかに翻訳すると、
このクラスは、複数のスレッドが統計の収集には使用されるが、きめ細かい同期制御を目的としていない共通の合計を更新する場合、一般にAtomicLong
よりも優先されます。 。更新競合が少ない場合、これら 2 つのクラスは同様の特性を持ちます。ただし、競合が多い状況では、このクラスの予想スループットは大幅に高くなりますが、スペース消費量が増加します。
つまり、LongAdder
は同時実行性が高い場合に効率的ですが、その代償としてスペースと時間のトレードオフが発生します。
LongAdder
の原理を簡単に説明しましょう。同時実行性がほとんどない場合は、1 つの変数base
に対して累算演算を実行するだけで十分です。AtomicLong
も同様ですが、同時実行性が増加した場合、変数base
を引き続き操作すると、多くのスレッドがブロックされるため、配列cells
を作成します。 in 配列の各要素は累積できます。最終結果を計算するときは、base
とcells
配列の各要素の合計を計算するだけです。スレッドが動作する配列内の特定のビットは、hash
を計算してインデックス位置を決定することで決定できます。
LongAdder
親クラスStriped64
から継承された属性。ここではCell
が内部クラスに使用されます。累積操作の には、累積値を格納するための内部value
属性があります。
// CPU核心数 static final int NCPU = Runtime.getRuntime().availableProcessors(); // 并发高时进行累加的Cell数组 transient volatile Cell[] cells; // 多个线程没有竞争时在base上进行累加 transient volatile long base; // Cell数组是否正在创建或扩容 transient volatile int cellsBusy;
累積演算メソッドincrement()
実際の呼び出しはadd(1L)
なので、add
メソッド##を直接見てみましょう。 #
public void add(long x) { Cell[] as; long b, v; int m; Cell a; if ((as = cells) != null || !casBase(b = base, b + x)) { boolean uncontended = true; // 表示没有竞争 if (as == null || (m = as.length - 1) < 0 || (a = as[getProbe() & m]) == null || !(uncontended = a.cas(v = a.value, v + x))) longAccumulate(x, null, uncontended); } }
ifステートメントを見てみましょう。初期条件では、
cellsは
nullであるため、
casBase操作はつまり、
base変数に対して累積が実行され、操作が成功した場合は、現在競合がいないことを意味するため、終了します。
casBaseメソッドが失敗する可能性があるため、この時点で2回目の
if文の判定が入ります。
Cellarray
asは
nullであるため、
##、Cell
配列as
を初期化し、インデックス1
で1
を累積します。
ステートメントas
を実行すると、null
にはならず、配列の長さも0 より大きくなります。
、この文を簡単に理解すると、配列as
位置、位置の値がnull
であるかどうかを判断し、null
である場合はlongAccumulate
を実行し、## でない場合はlongAccumulate
を実行します。 #null、判断を続けます
!(uncontended = a.cas(v = a.value, v x))この文は、累積見つかったインデックス位置で操作が実行され、成功した場合は操作が終了し、失敗した場合は
longAccumulate
以上がJava 並行プログラミングの LongAdder ソース コード分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。