Javaの並行処理では、私の理解では、一般的なものである並行処理で複数のスレッドのリソースと同じ操作、保証スレッドセーフの場合、それが複雑であるかどうかに応じて、特性の3種類 :
- 原子性:操作が中断されていません。複数のスレッドで行われた場合でも、一度の操作は、一度開始し、他のスレッドを妨げることはありません。
- 注文:オーダーコードに整然と行われ、順次実行プログラム。
- 可視性:複数のスレッドが同じ変数にアクセスすると、スレッドはこの変数の値を変更し、他のスレッドが直ちに変更された値を見ることができます。
しかし、一般的には安全ではない複数のスレッドを同期しませんでした。例えば、++型int iの自己増力機能の例。
int count = 0;
private void test() {
//创建10个子线程
List<Thread> threads = new ArrayList<Thread>();
for (int j = 0; j < 100; j++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
count++;
}
}
});
threads.add(thread);
}
for (Thread thread : threads) {
thread.start();
}
for (Thread thread : threads) {
try {
// 等待所有子线程执行完成 才运行主线程显示
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Log.e("count",count+"");
}
100個のスレッドを作成する必要があり、通常のロジックと私の運転の各スレッドによると、最終的な結果は、100,000でなければなりませんが、テストした後、あなたは、これは結果ではない、との結果が毎回異なっていることがわかります。
この理由は、それがスレッド安全でないため、不安のiが発生する可能性がメンバ変数であるということである非アトミック操作を、変数エラーが発生した、この変数の値を変更すると同時に、別のスレッドが発生しました。
これを避けるために、我々はできるJavaで、原子を操作することができ、ロックや巡回CASアトミック動作モードを実装します。
CASサイクルは、アトミック操作を使用して実装しました
はじめに:
- CAS(コンペア・アンド・スワップ)と別の比較、技術が同時に実行スレッドで使用されています
- CASは、並行性、安全を保証するために、アトミック操作であり、かつ同時の同期を保証することはできません
- CASは、CPUの命令である(JNIはネイティブメソッドを呼び出す必要があり、CPUの命令がコールします)
- CASは、ノンブロッキング、軽量楽観的ロックであります
CASの利点:
- システムのスループットを向上させるためにロックのオーバーヘッドを排除し、資源競争力の弱い状況で高い性能を達成するためにCPUの命令により、軽量楽観的ロックをノンブロッキング
CAS短所:
- 長いサイクル時間の大きな支出。時間が成功したスピンCASでない場合、それは非常に大きなCPUの実行コストをもたらすでしょう。
- アトミック操作は、共有変数を保証することができます。共有変数の操作を実行すると、我々は原子動作を保証するために、CASサイクル・アプローチを使用することができますが、複数の共有変数がサイクルを操作するときCASは、アトミック操作を保証するものではありません
- ABAの問題(確率は非常に小さいです)。CASは変更されませんが、更新された場合、変更されませんが、値は、Bとなっている場合、彼はAである値の下で動作時の値をチェックする必要があるので、あなたは、CAS検査を使用するときにいることがわかりますその値が変更され、実際に変更されていません。
実質的にその更新int型のために、最も簡単な方法は、アトミック変数を使用することである上、JDK 1.5は、原子操作は、java.util.concurrent.atomic袋のクラスを提供するために開始された原子タイプの4種類に適用されます。
- アトミック更新の基本タイプ
- アトミック更新の配列
- 原子更新引用
- アトミック更新属性(フィールド)
オペレーティングカテゴリ:
- AtomicBoolean:アトミック更新ブール
- AtomicInteger:整数原子更新
- AtomicLong:ロングアトミック更新
ここでは、incrementAndGetのAtomicInteger()メソッドはintであり、数1を追加します。
// static int count = 0;
static AtomicInteger atomicInteger;
private void test() {
atomicInteger = new AtomicInteger(0);
List<Thread> threads = new ArrayList<Thread>();
for (int j = 0; j < 100; j++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
// count++;
// 以原子方式将当前值加 1。
//addCount()
atomicInteger.incrementAndGet();
}
}
});
threads.add(thread);
}
for (Thread thread : threads) {
thread.start();
}
for (Thread thread : threads) {
try {
// 等待所有子线程执行完成 才运主线程
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// Log.e("count",count+"");
Log.e("count", atomicInteger + "");
}
次のように私たちは、5回を実行します。
私たちは、int型の原子を扱うことになる、すべての出力は、当社の事前の同時効果に沿って10万であることがわかります。実際には、原子性を保証するために内部的に安全でないクラスのCAS動作が使用されています。
例を挙げましょう:
//使用CAS 实现自增
private void addCount() {
for (;;) {
int count = atomicInteger.get();
boolean b = atomicInteger.compareAndSet(count , ++count );
if (b) {
break;
}
}
}
これは無限ループで、現在の値、および複数の大きな自己を所有するために割り当てられた一定の値を取得していき、それが成功するまで成功したジャンプは、連続ループを成功しない場合、その理由は、割り当てが失敗したということです他のスレッドも、この変数の値を変更したとき。もちろん、そのような操作は、内部供給源です。
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* @param expect the expected value
* @param update the new value
* @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int expect, int update) {
return U.compareAndSwapInt(this, VALUE, expect, update);
}
使用するソースはcompareAndSwapInt()クラスCAS安全でない操作の方法でした。
ロック(同期)アトミック操作を使用します:
Javaでの各オブジェクトが(JVMに依存して)ロックとして使用することができ
、通常の同期方法1.、ロックオブジェクトの現在のインスタンスで
同期静的方法2.、ロックは現在のクラスのクラスのオブジェクトであり、
ブロック同期方法について3.ロックが同期されますカッコ内のオブジェクトの設定
補足:例外が発生したときに、JVMが自動的に現在のスレッドによって占有されているロックを解除します同期方法やコードの同期ブロックについては、それが異常な現象にデッドロックにつながることはありません。
備考:
- 同期方法と静的同期方法はACC_SYNCHRONIZED修飾子を達成する方法に依存しています。同期改質による方法のJVM実装。メソッドが呼び出されると、呼び出し命令セット、実行スレッドは、第1のモニタを取得する場合は、フラグが設定されているかどうかを確認に成功し、そのリリースのモニター後メソッドを実行した後にメソッド本体を実行するために、アクセス方法をACC_SYNCHRONIZEDます。メソッドの実行中は、他のスレッドは、もはや同じモニターオブジェクトを取得することはできません。
- 実装されているシンクブロックmonitorenterとmonitorexit命令を使用して、同期バイトコードレベルのスコープを制御するために、それによって、リスナーオブジェクトのシンクブロックの領域で取得し解放ロックを通過します。
private void test() {
List<Thread> threads = new ArrayList<Thread>();
for (int j = 0; j < 100; j++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
addCount();
}
}
});
threads.add(thread);
}
for (Thread thread : threads) {
thread.start();
}
for (Thread thread : threads) {
try {
// 等待所有子线程执行完成 才运主线程
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Log.e("count",count+"");
}
//上锁
private synchronized void addCount() {
count++;
}
唯一のスレッドと同時に、その動作には、原子性を確保するように、私は)上記のコードは同期addCount(ロックされているこの方法を使用します。
次のように出力結果は以下のとおりです。
概要
両方の方法は、アトミック動作を同期およびJava並行処理でロックCASもベテランであり、他のスレッドがリソースにアクセスできないように、それらのリソースをロックするスレッドを使用する場合、およびまでロック解除後にアクセスすることができます。原子成形、長整数、およびブール変数または削除操作ができるのAtomicInteger、AtomicBoolean AtomicLongとアトミックオペレーションの他の種類を達成する、原子の他の種類のオブジェクトも安全でない他のクラスによって提供されていることを確実にするために望まれる場合アトミック文では、これはスレッドセーフで、他よりも優れた性能を提供することができます。もちろん、同期Javaは、便利で簡単なパフォーマンスと良いものを提供され、使用も一般的であるので、我々は使用することができ、スレッドの数が多すぎる場合、CASはそれ、複数のスレッドがされていないが、サイクルCASインターフェイスを呼び出していますスレッドによって引き起こされる妨害をさせますが、パフォーマンスが良い同期であるので、CPUは、あまりにも深刻な消費。