blog

Java juc メモ [1] - atomic パッケージ

アトミックパッケージの下のパッケージは、クラスの原子操作の可能な限りロックフリーの実装として並行シナリオのシリーズを提供し、コアのアイデアは、CASを使用することです+軽量楽観的ロックのループの実装は...

May 27, 2020 · 7 min. read
シェア

Atomicパッケージの紹介と分類

java.util.concurrentアトミックパッケージの下のパッケージは、クラスの原子操作の可能な限りロックフリーの実装として並行シナリオのシリーズを提供し、コアのアイデアは、CASを使用することです+軽量楽観的なロックのループ実装は、並行競争の場合には激しい効率ははるかに実装をロックするよりも良くなりますされていません。著者の完了を支援するためにJCPのJSR - 166専門家グループのメンバーによってStriped64に加えて、ダグレアマスターです。jucパッケージの全体的なコードボリュームは大きくありませんが、新しい考え方や収穫を見るたびに。

アトミックパッケージングクラス:

  • AtomicBoolean
  • AtomicInteger
  • AtomicLong
  • AtomicReference
  • AtomicIntegerArray
  • AtomicLongArray
  • AtomicReferenceArray
  • AtomicStampedReference
  • AtomicMarkableReference

属性更新クラス:

  • AtomicIntegerFieldUpdater
  • AtomicLongFieldUpdater
  • AtomicReferenceFieldUpdater

高度並行コンピューティングクラス:

  • Striped64
  • LongAdder
  • DoubleAdder
  • LongAccumulator
  • DoubleAccumulator

プレゼンテーション

原子パッキングクラス

いくつかの一般的な属性:

  • serialVersionUID 連載バージョン番号
  • 特定の CAS 操作を実装する安全でないオブジェクト
  • valueOffsetはCASオペレーションのコンフィギュレーションに使用され、レコードはvalueの特定のアドレスであり、特定のCASオペレーションのアドレスとして理解することができます。
  • 値 volatileで変更される実際の値で、この値への変更は他のスレッドからすぐに見えるため、アトミックパッケージが存在します。

いくつかの一般的な方法:

  • get 現在値の取得
  • set は元の値に関係なく現在の値を設定します。
  • compareAndSet CAS現在値の設定
  • lazySet 値の遅延設定はメモリバリアーを減らしますが、設定された値は他のスレッドからはすぐには見えません。
  • getAndSet get+set

アトミックブール

compareAndSwapObject内部的にboolean型の代わりにint型を使用し、booleanに対するアトミック演算を提供します。クロスプラットフォーム機能により、JAVAのUnsafeメカニズムの最小演算型はint型であると考えられています。を提供するだけです。

AtomicInteger

アトミックロング

AtomicIntegerとほぼ同じで、compareAndSwapLongループ操作の基礎となる、longに対するアトミック操作を提供します。

同時に、現在のVMがロング・タイプのストレートCASをサポートしているかどうかが判断されます。

原子参照

unsafe.getIntVolatileオブジェクト参照に対するアトミック操作の提供についても同様で、ループ操作を基本としています。

AtomicIntegerArray

int[]型の基礎となる操作は、配列要素のオフセットを計算することによって、対応する要素値に対してアトミックな操作を実行するもので、基礎となる配列がメモリ内で物理的に連続している必要があります。int[]オブジェクトの内部要素は揮発性ではないため、つまり内部要素は他のスレッドから直接見えないため、各要素に対する操作は次のような安全でないメソッドによって達成されます。

AtomicLongArray / AtomicReferenceArray

考え方はAtomicIntegerArrayと同じです。

AtomicStampedReference/AtomicMarkableReference。

アトミックなラッパークラスであることに変わりはありませんが、ラップされるオブジェクトは単一の値から値の集合に変更されています:

  • AtomicStampedReference<V> ペアをパック、バイナリ グループ処理、全体のペアの統一 CAS 処理として理解することができます、CAS ABA 問題を解決できます。
  • AtomicMarkableReference 上記のように、バイナリは<V, boolean>

AtomicFieldUpdater

AtomicFieldUpdater は、アトミック・ラッパークラスの機能を分割し、値が保存される特定の場所をアトミック操作から分離します。例

public class Test {
 // 場所を保存する
 private volatile int value;
 // できればstatic final修飾子で場所を更新する
 private static final AtomicIntegerFieldUpdater<Test> valueUpdater = AtomicIntegerFieldUpdater.newUpdater(Test.class, "value");
 public void printValue() {
 System.out.println(value);
 }
 public void incrementValue() {
 //value++; // 明らかに、これを行うことは許可されていない
 valueUpdater.incrementAndGet(this);
 }
 public static void main(String[] args) {
 Test test = new Test();
 test.printValue();
 test.incrementValue();
 test.printValue();
 }
}

分離後、valueは、同時フェッチやその他の操作では、ノンラッパークラスとして操作することができますが、 同時アップデート操作では、アップデータを介して操作する必要があります。

アップデータの原理:

  1. AtomicFieldUpdaterクラスは抽象クラスであり、CASの具体的なメソッドは抽象メソッドであり、内部クラスはこの抽象クラスを継承してCAS関連のメソッドを実装しています。
  2. AtomicFieldUpdaterクラスが提供するメソッドは、 AtomicWrapperクラスが提供するメソッドと同じです。
  3. 内部クラスは、リフレクションによって本当に操作する必要があるターゲット・プロパティを取得し、コンストラクタのシグネチャはAtomicIntegerFieldUpdaterの例として取り上げられます:AtomicIntegerFieldUpdaterImpl(final Class<T> tclass, final String fieldName, final Class<?> caller)
  4. アトミック操作はリフレクションによって実装されるため、アップデータは属性の可視性や型チェックなど、多くの検証操作を実行する必要があります。特に、accessCheckは各アトミック操作の前に実行され、入力呼び出し元のオブジェクトがコンストラクタ・メソッドで渡されたクラスと同じであることを確認します。

原理からわかるように、AtomicFieldUpdaterを使用するには、いくつかの規約に従う必要があります:

  • 操作のプロパティは volatile で変更する必要があります。
  • 操作のプロパティは、アップデータから見えるようにする必要があります。
  • アップデータはリフレクションを通してインスタンスのプロパティを操作するため、操作されるプロパティはスタティックでは変更できません。
  • オペレーションのプロパティをfinalで変更することはできません。
  • 例えば、long 型は AtomicLongFieldUpdater に対応します。
  • そうでなければ、アトミック・ラッパークラスを直接使用する方が良いでしょう。

実際には、アップデータ操作の使用とアトミックラッパークラスの直接使用は同じ役割を持って、同じ機能を達成することができますが、リフレクションの使用により、ターゲットオブジェクトのプロパティを取得し、各操作をチェックする前に行う必要があるため、アップデータがより制限されるだけでなく、パフォーマンスがアトミックラッパークラスの直接使用ほど良くありません。では、なぜアップデータを使うのでしょうか?つの合理的な使用シナリオがあります:

  1. ほとんどの場合、ラップされていないクラスとして使いたいものですが、アトミック操作で更新する必要がある場合もあります。
  2. アップデータの静的に変更されたインスタンスは、呼び出し元のすべてのオブジェクトで動作するため、アトミック・ラッパークラスを使用すると、非ラッパークラスよりも多くのメモリを消費します。

AtomicIntegerFieldUpdater / AtomicLongFieldUpdater / AtomicReferenceFieldUpdater

それぞれint/long/Objectに対応するプロパティの更新。

AtomicLongFieldUpdaterここで、VMがロング・タイプのCAS操作をサポートしているかどうかを上記のAtomicLongで判断した結果、2つのアップデータが提供されます:

 if (AtomicLong.VM_SUPPORTS_LONG_CAS)
 return new CASUpdater<U>(tclass, fieldName, caller);
 else
 return new LockedUpdater<U>(tclass, fieldName, caller);

AtomicReferenceFieldUpdaterまた、可視性のためのaccessCheckに加えて、valueCheckによってnull値や値の型をチェックします。

高同時計算クラス

Javaのコンカレント・アキュムレーターの記事で紹介されているので、その一部をそのままコピーします。

ストライプ64

内部には、アトミック・ラッパークラスの機能のサブセットであるサブクラスCellが含まれており、値とそのCAS機能のみを保存し、getやincrementAndGetなどの他の機能は保存しません。また、Cellクラスは@Contendedアノテーションを使用し、擬似共有の問題を回避しています。

transient volatile long base内部的には、「値の一部分を保持する」ために定義され、「値のもう一部分を保持する」ために一時的に揮発するCell[]セルが定義されます。そう、ベースとセルを合わせて最終的な値を構成するのです。

より高い並行性レベルでは、アトミック・ラッパークラスによって使われるCASオペレーションも、より頻繁に失敗し、より多くの不要なリソースを消費し、パフォーマンス低下につながります。

Striped64は、アトミック・ラッパークラスではCASが単一のリソースを競合するという事実を考慮し、競合が発生した場合に競合するリソースを分散させることを選択し、各スレッドにCellを割り当て、各リソースが対応するリソースを競合させることで、競合を大幅に削減し、特定の高同期シナリオにおいてアトミック・ラッパークラスを大幅に凌駕します。詳しくは古い記事をご覧ください。

ロングアダー / ダブルアダー

Striped64を継承し、機能的には単純にNULLバイナリ計算、つまりデフォルトの加算を使用したアキュムレータに必要なメソッドを実装しています。詳細は古い記事を参照してください。

ロングアキュムレータ / ダブルアキュムレータ

上記と同じですが、カスタムバイナリ計算方法を使用しています。

この記事は、お気軽にご覧ください!

Read next

インパクトの大きい配列の重複排除

forループ\n\nES678の登場です!\n配列から始めましょう\nvar a = [\n 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1\n 0x1, '1', '1', '1'\n '1', '1', '1', '1', '1', '1', '1'\n '1', 'null,'\n null、null、null、null、null、null、null、null、null、null\n null, undefine\n 未定義

May 27, 2020 · 2 min read