はじめにアトミック操作クラス
並行処理は並行プログラミングにおけるセキュリティ上の問題を起こしやすい、非常に簡単な例があり、マルチスレッドはi = 1から変数を更新し、私は複数のスレッドが操作を実行するような++、正しい値、およびこの問題未満を得ることが可能であるが、ほとんどの一般的に使用される方法は、(目的のスレッドセーフを実現するために、同期によって制御される同期でこの記事を見ることができます)。しかし、同期のために、ペシミスティック・ロック・ストラテジーを使用して、特に効率的な解決策ではありません。実際には、JUC原子パケットで操作の簡単なシーケンス、高い性能を提供し、変数の基本的なタイプを更新するために、スレッドの安全性クラスを確保するために、アレイ素子、および参照フィールドタイプのオブジェクトタイプを更新します。これらのクラスは、原子パケットのJava CAS操作でデータを更新するために楽観的ロック戦略原子は特定の実装の使用であるに使用されます。
予選-CAS操作
原理は、原子クラスパッケージでこれらの原子の動作を理解することができ、我々は最初のCAS操作が何であるかを理解する必要があります。
CASは何ですか?
ロックを使用する場合、スレッドは、ロックがコードのクリティカルセクションの実行ごとに競合を作成できることを前提とペシミスティック・ロック・ストラテジー、そうもロックを取得するために他のスレッドをブロックするロック時間を取得する現在のスレッドで取得します。すべてのスレッドが共有リソースにアクセスすると仮定すると(また、ロックフリー操作としても知られる)CAS動作は、楽観的ロック戦略で競合が天然に存在しないので、矛盾しない他のスレッドの動作を妨げません。したがって、休止状態をブロックしないスレッドが発生します。衝突があったのであれば、どのように行うには?いいえロック操作が競合があるかどうかを識別するために、交換を比較し、スレッドとしても知られている(比較およびスワップ)CASを使用することなく、競合は競合が存在しなくなるまで、現在の操作を再試行するように思われました。
CASの動作中
CAS交換比較処理は、人気のCAS(V、O、N)として理解することができる、3つの値がある:メモリアドレスに格納された実際の値V、Nの新しい更新値; O期待値(古い値)。VとOが同じである場合、それは古い値を言うことであり、同じメモリの実際の値は、値が前に別のスレッドによって変更されていないことを示し、つまり、古い値Oは現在、最新の値のためである、と自然にNの新しい値を割り当てることができますV.へ 逆に、VとO値は、新しい値V Nを割り当てることはできませんので、Vを返すことができ、古い値がO値の最新バージョンではありません、裏返しにする別のスレッドとなっていることを示し、同じではありません。CASの運転変数を使用して複数のスレッドが、1つのスレッドのみの更新に成功し、成功する場合は、残りの部分は失敗します。スレッドはもちろん、スレッドを一時停止するように選択することができ、失敗し再試行します。
仮想マシンは、プロセッサの実装によって提供されるJDK1.5 CMPXCHG命令を使用する後CAS実装は、サポートハードウェア命令セットを必要とします。
シンクロナイズドVS CAS
主な問題は、(非最適化前の)同期のベテランである:それは(同期をブロックする)相互に排他的で同期しているので、そこにパフォーマンスの問題になると、スレッドの競争の存在をもたらしたロックのスレッドブロックをウェイクアップします。CASは、任意のスレッドではないCASは、特定の操作が試みに失敗した実施ではなく、保留中の操作の時間がかかる覚醒、したがって、非ブロッキング同期と呼ばれる時ハング。これは、二つの主な違いです。
CASの問題
-
ABAの問題
CASは、古い値が変更されていないチェックしますので、ここで興味深い質問があります。例えば、BにAへの古い値は、次にAに、単に古い値を行い、CAS検査は何の変化はまだありません明らかにしたが、実際には変更をしました。ソリューションは、共通のデータベース楽観的ロックの道をたどるバージョン番号を解決することができます追加することができます。元のパス変更A-> B-> Aは1A〜> 2B-> 3Cなります。 -
あまりにも長いスピン
CASを使ってノンブロッキング同期は、そのスレッドではありませんが中断され、それは(無限ループ以上のもの)をスピンしませんパフォーマンスに長すぎるここでスピンが大きな消費であれば、次の試みでした。JVMは、プロセッサによって提供される一時停止命令をサポートできる場合は、それほど効率のいくらかの改善が存在することになります。
アトミック更新の基本タイプ
アトミックアトミック更新パッケージは、ツールの基本的な種類を改善するために、これらがあります。
- AtomicBoolean:アトミック更新ブールを更新。
- AtomicInteger:アトミック更新整数を更新。
- AtomicLong:ロング原子を更新するための更新。
ここでは例のAtomicIntegerは、一般的に使用される方法を要約するためにこれらのクラスは、基本的に同じ使用されています
- addAndGet(INTデルタ):インスタンスを追加することによってアトミック入力値と元の値、及び最終的な結果を返します。
- incrementAndGet()は:元の値の例原子の方法でインクリメントされ、加算の最終結果を返すしています。
- getAndSet(int型newValueに):インスタンスの値が新しい値に更新し、古い値を返しています。
- getAndIncrement():アトミック例1の正面自己増力の古い値を返し、元の値に加えます。
あなたがそれらを繰り返して、APIを見ることができないいくつかの方法があります。getAndIncrementメソッドに実装原理のAtomicIntegerを理解するためには、例えば、ソースコードを見て:
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
図から分かるように、この方法は、実際には安全でないクラスgetUnsafe安全でないインスタンスを取得するとき静的メソッドによって取得された、方法getAndAddInt安全でないインスタンスに呼び出されます。
private static final Unsafe unsafe = Unsafe.getUnsafe();
クラスsun.miscパッケージにおいて安全でない、Unsaferクラスは、基礎となるオペレーティングを提供し、主に原子パッケージでcompareAndSwapInt危険操作クラスクラス原子によって提供され、compareAndSwapLong CAS動作を実現する方法の一連を提供します。AtomicIntegerの使用を示すために、ここでは簡単な例:
public class AtomicDemo {
private static AtomicInteger atomicInteger = new AtomicInteger(1);
public static void main(String[] args) {
System.out.println(atomicInteger.getAndIncrement());
System.out.println(atomicInteger.get());
}
}
出力
1
2
非常にシンプルなの例は、新しいのAtomicIntegerオブジェクトが構築されているのAtomicInteger方法は、そのパッケージを基本的なデータ型を渡すことです。そのようなインクリメント、デクリメント、追加、更新、およびその他の操作などの基本的な変数の動作は、のAtomicIntegerはまた、これらの操作のための対応する方法を提供します。ただし、そのデータを確保するために設けられたCAS動作危険のAtomicInteger助ためときにスレッドセーフ更新され、CASは、オプティミスティック・ロック・ストラテジーを使用することであるので、従って、データ更新のこの方法は、高い効率を有します。
AtomicLong原則と一貫性ののAtomicIntegerが、int型の変数の変数1のために長いです。更新されたクラスAtomicBooleanクラスboolean型変数は、それが更新さを実現する方法ですか?コアは、compareAndSet
T法、そのソースコードは以下の通りであります:
public final boolean compareAndSet(boolean expect, boolean update) {
int e = expect ? 1 : 0;
int u = update ? 1 : 0;
return unsafe.compareAndSwapInt(this, valueOffset, e, u);
}
図から分かるように、実際に、それは最初に達成するcompareAndSwapIntのint型変数のアトミック更新方法続く整数変数のcompareAndSet 0,1方法、に変換されます。それだけ原子パケットは、原子ブール、INT、アップデートの長い3つの基本タイプ、ブールように更新された基準、アトミック更新チャー、douleの方法が提供されて見ることができ、フロートは、同様の考え方を用いて実施することができます。
アトミック更新アレイタイプ
配列内の原子の要素を提供するパッケージはクラスがある原子を更新することができます。
- AtomicIntegerArray:アトミック更新要素整数配列。
- AtomicLongArray:ロングアトミック更新素子アレイ。
- AtomicReferenceArray:アトミック更新基準配列要素タイプ
これは一般的な方法を要約するAtomicIntegerArrayに関しては、用法のいくつかのカテゴリと一致しています。
- addAndGet(INT I、INTデルタ):アトミック入力値を有する要素の配列インデックスiと方法を更新します。
- getAndIncrement(INT i)は:アトミック要素の配列インデックス1からIが増加するにつれて方法を更新します。
- compareAndSet(int型のI INTは、INTの更新を期待する):配列内の更新インデックスは、要素位置Iであります
図から分かるように、AtomicIntegerArrayのAtomicIntegerのと一致する方法、しかしはAtomicIntegerArrayのプロセスで指定されたビット配列のインデックスi以上であろう。以下は簡単な例を与えます:
public class AtomicIntegerArrayDemo {
private static int[] value = new int[]{1, 2, 3};
private static AtomicIntegerArray integerArray = new AtomicIntegerArray(value);
public static void main(String[] args) {
//对数组中索引为1的位置的元素加5
int result = integerArray.getAndAdd(1, 5);
System.out.println(result);
System.out.println(integerArray.get(1));
}
}
出力
2
7
要素番号1 + 5前方法getAndAdd位置により、素子1の結果から分かるように処理を戻すも2まで追加、7なりました。
アトミック更新参照型
あなたは、スレッドの安全性を確保するために、アトミック参照型の変数を更新する必要がある場合、原子はまた、関連するカテゴリが用意されています。
- AtomicReference:アトミック更新参照型、
- AtomicReferenceFieldUpdater:参照フィールドの原子更新種別、
- AtomicMarkableReference:参照型原子マーク位置を更新します。
これらのクラスは、同じ基本的な方法は、これらのクラスの基本的な使用方法を説明するために例をAtomicReferenceすることである使用します。ここではデモがあります
public class AtomicReferenceDemo {
private static AtomicReference<User> reference = new AtomicReference<>();
public static void main(String[] args) {
User user1 = new User("a", 1);
reference.set(user1);
User user2 = new User("b",2);
User user = reference.getAndSet(user2);
System.out.println(user);
System.out.println(reference.get());
}
static class User {
private String userName;
private int age;
public User(String userName, int age) {
this.userName = userName;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
", age=" + age +
'}';
}
}
}
出力
User{userName='a', age=1}
User{userName='b', age=2}
まず、オブジェクトがAtomicReference、ユーザー1でカプセル化され、その後、それが結果から分かるgetAndSetメソッドを呼び出して、プロセスになり、基準原子ユーザーオブジェクトを更新しUser{userName='b', age=2}
、それが元のユーザがユーザオブジェクト返します{userName='a', age=1}
。
アトミック更新フィールドタイプ
あなたがフィールドオブジェクトを更新するために、マルチスレッドの場合は、スレッドセーフを保証するために必要がある場合、原子はまた、アトミックオペレーションの対応するクラスを提供します。
- AtomicIntegeFieldUpdater:アトミック更新整数フィールドタイプ;
- AtomicLongFieldUpdater:アトミック更新長い整数フィールドタイプ;
- AtomicStampedReference:アトミック更新参照型は、このアプローチは、バージョン番号を更新します。そして、なぜABAの問題を解決する際に、バージョン番号、CASで更新されます。
2つのステップを必要とするアトミック更新フィールドを使用します。
- アトミック更新フィールドクラスは、静的メソッドを介して、抽象クラスである
newUpdater
アップデータを作成するための、およびクラスを設定する必要があり、更新したい属性。 - 更新クラス属性がなければなりません
public volatile
修正されます。
これらのクラスは、特定の使用を見て例をAtomicIntegerFieldUpdaterための一貫した方法を提供します。
public class AtomicDemo {
private static AtomicIntegerFieldUpdater updater = AtomicIntegerFieldUpdater.newUpdater(User.class,"age");
public static void main(String[] args) {
User user = new User("a", 1);
int oldValue = updater.getAndAdd(user, 5);
System.out.println(oldValue);
System.out.println(updater.get(user));
}
static class User {
private String userName;
public volatile int age;
public User(String userName, int age) {
this.userName = userName;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
", age=" + age +
'}';
}
}
}
出力
1
6
作成する、例から分かるようにAtomicIntegerFieldUpdater
静的メソッドを、それが提供作成し、getAndAdd
値は、前添加法に戻り、入力フィールドで指定された値を加算し、。元の値の年齢フィールドにユーザーオブジェクトは、5を加えた後、ユーザオブジェクトの年齢フィールドの値は6となっていることが分かります。