スレッドセーフ
並行プログラムの正しさに代わってスレッドの安全性は、マルチスレッド環境で参照する、アプリケーションが常に正しい行動を発揮することができるようになります。
問題の根
共有変数の状態:スレッド安全性の問題のすべてが、同じ原因に起因することができます。「共有」は変数が複数のスレッドによって同時にアクセスすることができることを意味し、「変数」は、変数の変更の値がそのライフサイクル中に発生する可能性があることを意味します。
共有可変状態アクセス動作にコアでスレッドセーフなコードを記述すること導入することによって管理されている同期機構を共有可変状態にいつでもアクセスでその唯一のスレッドを確保します。
共有変更可能なオブジェクト
並行プログラミングでは、共有変数の目的は、以下の3つの質問に直面するだろう。
不可分性
レース条件
計算の精度は、複数のスレッドの順序に依存する場合交互に行われるので、競合状態が発生する可能性があります。静的モードの一般的な2種類:
- リードモディファイライト(読み出し - 変更 - 書き込み)の手順洗練操作:共有変数値(読み取り)を読んで、その後、計算をやって(修正)が値に基づいて、その値(書き込み)共有変数を更新します。
- チェック-THEN-行為(動作を監視した後)の精緻化ステップ:共有変数の値を読み取るには、この変数の値は、はいに応じてどのような次のアクションを決定します。
リードモディファイライトの例のを:
@NotThreadSafe
public class UnsafeCounter {
private int value;
public int getNext() {
return value++;
}
}
复制代码
値のインクリメント演算子++は三つのステップ、前記値の値を読んで、プラス1の値を、最終的な計算結果は、vlue値、典型的な読み取り書き換えレースモードに書き込まれます。
チェックイン後、-行動例を:
@NotThreadSafe
public class UnsafeSequence {
private int sequence;
public int getSequence() {
if(sequence >= 99){ // 步骤1 检查共享变量
return 0; // 步骤2 act 检查后操作
} else {
return sequence++;
}
}
}
复制代码
共有変数の修正プロセス内の特定のスレッドは、他のスレッドが動作することができないことを保証するためにアトミック操作を導入する必要があり、競合状態の問題を回避する方法、他のスレッドがアトミックアトミック操作の前または後に共有変数の状態を読み込み、変更することができます。ここでは、原子操作が何であるかを見てみましょう。
原子操作
ビューのスレッド以外のスレッドからの操作が不可分である場合、共有変数へのアクセスを伴う操作は、操作アトミック操作は、操作がアトミックです。参照の概念から、次の点に注意してください。
- アトミック操作は、共有変数の操作へのアクセスのためのものであり、それはローカル変数の原子かどうかは関係ありません。
- アトミック操作は、マルチスレッド環境のために理にかなっています
二つの意味以下の不可分同時に:
- アクセス(書き込み、読み出し)ビューの実行ポイントのスレッド以外のスレッドから共有変数は、この操作は、いずれかの終了または、まだ起きていない。この状態では存在しない行われています
- ずらすことができない共有の操作変数のアクセス統合セット。
2つの方法のJavaのアトミック:
- ロック(ロック)を使用して1つのスレッドしかアクセスすることができ、いつでも共有変数を保護するために従事することができます排他ロック(ロックについては、単一の章を共有する必要があります)があります。
- このレベルを達成するためにハードウェアプロセッサおよびメモリ内で直接CAS命令、CASコマンドを使用し、(CASは、別々の章を共有する)ハードウェアロックと見なすことができます。
可視
共有変数のスレッドが更新された後、マルチスレッド環境では、変数スレッドへのフォローアップの訪問でも恒久的に、更新された結果を読み取ることができませんすぐに更新された結果を読み取ることができない場合があります。可視性:これは、別の症状スレッドセーフです。
問題の可視性は、Javaのメモリモデル、特定の参照の決定によるものであるJavaの並行プログラミング-メモリモデルの
視認性を確保するためにどのようにJavaプラットフォーム:
- volatileキーワードは、通知が別のスレッドの動作変数を更新することを保証するために使用されます
- ロック機構は、両方の視認性を確保することができ、アトミック性を保証することができます
- 最後の可視性:コンストラクタ内のフィールドの最後の修正完了したら、その後、他のスレッドが最後のフィールドの値を見ることができます
整頓
現代のプロセッサに加えて、効率を改善するために(アウト・オブ・オーダ実行)によって順序外で実行するマイクロプロセッサ命令、JavaのJITエディタ自体は、リオーダ命令を実行し、マシン命令は、最終的にバイトを生成することができますコードシーケンスは一貫していません。並行プログラムは、順不同で実行できる文のマルチスレッド実行スレッドでは、このような次のプログラムなど、予期しない結果を引き起こすことがあり、命令の実行順序を変更、FLGが先行することが真= = 1を実行し、スレッド2が突然= 2を印刷することができます。
:シーケンシャルメモリアクセスをどのようにJavaプラットフォームを確保するために、
- メモリバリア命令の特定のタイプを追加することにより、プロセッサの並べ替えを無効にするには、揮発性のキーワード
- 並べ替えを禁止するロック
スレッドの安全性の問題を解決
共有状態を避けます
ステートレスオブジェクトはスレッドセーフな、典型的なサーブレットプログラムである必要があり、各サーブレット自体は、彼らがお互いに干渉しない、互いから分離され、状態を保持していません。ホールド状態が避けられない場合は、他のスレッドへのアクセスを防止するために、「隠された」の状態をスレッドシーリング技術を使用することができます。一般的なアプローチは、閉じたとThreadLoca二つの形式を積み重ねることです。クローズスレッドスタックは、特殊なケースを閉じ、ローカル変数はオブジェクトのみを介してアクセスすることができ、スタックに閉鎖され、これらのローカル変数は実行スレッドのスタック内に囲まれ、他のスレッドは、それらにアクセスすることはできません。
public int loadTheArk(Collection<Animal> candidates) {
SortedSet<Animal> animals;
int numPairs = 0;
Animal candidate = null;
// animals confined to method, don't let them escape!
animals = new TreeSet<Animal>(new SpeciesGenderComparator());
animals.addAll(candidates);
for (Animal a : animals) {
if (candidate == null || !candidate.isPotentialMate(a))
candidate = a;
else {
ark.load(new AnimalPair(candidate, a));
++numPairs;
candidate = null;
}
}
return numPairs;
}
复制代码
上記のコードでは、動物および候補のローカル変数は、この方法は、スレッドセーフであり、他のスレッドがアクセスする、逃げない、スタックフレーム内に制限されるべきです。そこにはThreadLocalの上の特別なセクションが導入され、説明が展開されていません。
変更可能な状態を避けます
その状態は、作成された後に変更することはできませんオブジェクト場合、オブジェクトは不変オブジェクトと呼ばれています。不変オブジェクトは、スレッドセーフでなければなりません。以下の条件が満たされると、オブジェクトは不変です。
- オブジェクトは、状態を変更できないことを作成した後
- すべてのプロパティは、最終的なObject型です
- オブジェクトが正しく作成されている(作成中に、これは逃れられない参照)
グアバライブラリはまた、ImmutabelList、これらのImmutableSetとして、不変クラスのセットを提供し、我々は可能な限りあなたのコードでそれらを使用する必要があります
同期メカニズム
スレッドの安全性を確保するために、同期メカニズム - 共有変数とは避けることができない場合は、唯一の最悪を使用しています。Javaコード、通常はsynchronizedキーワード、クラスやオブジェクトのロックでは、同期を達成します。次の章で分析するJava固有の同期メカニズム。