JavaスレッドJavaスレッド:アトミック
いわゆる原子量,すなわち操作変数の操作は「原子的」であり,この操作は再分割できないためスレッドが安全である.
なぜ原子変数を使用するのかは、複数のスレッドが単一の変数に対して操作してもいくつかの問題が発生するためです.Java 5の前にvolatile、synchronizedキーワードで同時アクセスのセキュリティ問題を解決できますが、これは面倒です.
Java 5以降、単一変数マルチスレッドの同時セキュリティアクセスを行うためのツールパッケージjavaが提供する.util.concurrent.atomic、その中のクラスも簡単です.
Atomicクラスの役割は、単一データの動作を原子化する を実現する. Atomicクラスを使用する複雑な構築であり、ブロックする必要のないコードアクセスは、2つまたは2つ以上のatomic変数(または単一のatomic変数に対して2回または2回以上の操作を行う)に対して通常、これらの操作が1つの原子 として機能するように同期する必要があると考えられる. staticを付けないと一人当たりの操作はすべて10000という値に対して行われ、これらのスレッドは他のスレッドが10000という値を変更していないときに行う操作なのか、それとも各スレッドがalongの値を修正しても他のスレッドには有効にならないのか. static:static変数はクラスに属し、ある対像ではなく、つまりすべての対像共通の変数であり、非staticは1対像の である.
では、Volatileはどのようにして可視性を保証しますか?x 86プロセッサの下でJITコンパイラが生成したアセンブリ命令をツールで取得し、Volatileを書き込み操作するCPUが何をするかを確認します.
Javaコード:
instance = new Singleton();//instanceはvolatile変数です
アセンブリコード:
0x01a3de1d: movb $0x0,0x1104800(%esi); 0x01a3de24: lock addl $0x0,(%esp);
volatile変数修飾のある共有変数が書き込み操作されると複数の2行目のアセンブリコードが作成されますが、IA-32アーキテクチャソフトウェア開発者マニュアルを調べると、lock接頭辞の命令がマルチコアプロセッサの下で2つのことを引き起こすことがわかります.現在のプロセッサキャッシュ行のデータがシステムメモリに書き込まれます. このメモリを書き戻す操作は、他のCPUでメモリアドレスをキャッシュしたデータが無効になる.
プロセッサは,処理速度を上げるためにメモリと直接通信するのではなく,まずシステムメモリのデータを内部キャッシュ(L 1,L 2,その他)に読み込んでから操作するが,操作が完了するといつメモリに書き込まれるかわからず,Volatile変数を宣言して書き込み操作を行うと,JVMはプロセッサにロックプレフィックスのコマンドを送信し,その変数がキャッシュ行にあるデータをシステムメモリに書き込む.ただし、メモリに書き戻しても、他のプロセッサのキャッシュ値が古い場合に計算操作を実行すると問題があるため、マルチプロセッサでは、各プロセッサのキャッシュが一致していることを保証するために、キャッシュ整合性プロトコルが実現され、各プロセッサはバス上に伝播したデータを嗅ぐことで、自分のキャッシュの値が期限切れであるかどうかをチェックし、プロセッサが自分のキャッシュ行に対応するメモリアドレスが変更されていることを発見すると、現在のプロセッサのキャッシュラインが無効に設定され、プロセッサがこのデータを変更しようとすると、システムメモリからプロセッサキャッシュにデータを読み直すよう強制されます.
なぜ原子変数を使用するのかは、複数のスレッドが単一の変数に対して操作してもいくつかの問題が発生するためです.Java 5の前にvolatile、synchronizedキーワードで同時アクセスのセキュリティ問題を解決できますが、これは面倒です.
Java 5以降、単一変数マルチスレッドの同時セキュリティアクセスを行うためのツールパッケージjavaが提供する.util.concurrent.atomic、その中のクラスも簡単です.
Atomicクラスの役割
package com.thread.atomic;
import java.util.concurrent.atomic.AtomicLong;
/**
*
*/
import java.util.concurrent.locks.Lock;
public class MyCount extends Thread{
private static AtomicLong cash = new AtomicLong(1000); // < static>
private Lock lock ;//
private int x;
private String name;
MyCount(Lock lock, String name, int x) {
this.lock = lock;
this.x = x;
this.name = name;
}
@Override
public void run() {
lock.lock();
cash.addAndGet(x);
System.out.println(name + " " + x + ", :" + cash.get());
lock.unlock();
}
}
package com.thread.atomic;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestAtomic {
private Lock lock = new ReentrantLock(false);//
public void run() {
ExecutorService pool = Executors.newCachedThreadPool();
MyCount c1 = new MyCount(lock, " ", 100);
MyCount c2 = new MyCount(lock, " ", -200);
MyCount c3 = new MyCount(lock, " ", 400);
MyCount c4 = new MyCount(lock, " ", -100);
MyCount c5 = new MyCount(lock, " ", -100);
MyCount c6 = new MyCount(lock, " ", 400);
MyCount c7 = new MyCount(lock, " ", -500);
pool.execute(c1);
pool.execute(c2);
pool.execute(c3);
pool.execute(c4);
pool.execute(c5);
pool.execute(c6);
pool.execute(c7);
pool.shutdownNow();
}
public static void main(String[] args) {
new TestAtomic().run();
}
}
private static AtomicLong cash = new AtomicLong(1000); // < static>
Volatileの実現原理
では、Volatileはどのようにして可視性を保証しますか?x 86プロセッサの下でJITコンパイラが生成したアセンブリ命令をツールで取得し、Volatileを書き込み操作するCPUが何をするかを確認します.
Javaコード:
instance = new Singleton();//instanceはvolatile変数です
アセンブリコード:
0x01a3de1d: movb $0x0,0x1104800(%esi); 0x01a3de24: lock addl $0x0,(%esp);
volatile変数修飾のある共有変数が書き込み操作されると複数の2行目のアセンブリコードが作成されますが、IA-32アーキテクチャソフトウェア開発者マニュアルを調べると、lock接頭辞の命令がマルチコアプロセッサの下で2つのことを引き起こすことがわかります.
プロセッサは,処理速度を上げるためにメモリと直接通信するのではなく,まずシステムメモリのデータを内部キャッシュ(L 1,L 2,その他)に読み込んでから操作するが,操作が完了するといつメモリに書き込まれるかわからず,Volatile変数を宣言して書き込み操作を行うと,JVMはプロセッサにロックプレフィックスのコマンドを送信し,その変数がキャッシュ行にあるデータをシステムメモリに書き込む.ただし、メモリに書き戻しても、他のプロセッサのキャッシュ値が古い場合に計算操作を実行すると問題があるため、マルチプロセッサでは、各プロセッサのキャッシュが一致していることを保証するために、キャッシュ整合性プロトコルが実現され、各プロセッサはバス上に伝播したデータを嗅ぐことで、自分のキャッシュの値が期限切れであるかどうかをチェックし、プロセッサが自分のキャッシュ行に対応するメモリアドレスが変更されていることを発見すると、現在のプロセッサのキャッシュラインが無効に設定され、プロセッサがこのデータを変更しようとすると、システムメモリからプロセッサキャッシュにデータを読み直すよう強制されます.