Javaマルチスレッドを同時にプログラムし、三つの要素を併発する。
2255 ワード
一、原子性
原子、もう一つの分割してはいけない粒子。原子性とは、もう一つ以上は分割できない操作のことです。
int i=1;//原子操作
i+;////原子以外の操作では、メインメモリからiをスレッドワークメモリに読み取り、+1を行い、更にiを朱メモリに書き込みます。
読み取りと書き込みは原子操作ですが、合わせて原子操作ではなく、「複合作業」とも言います。
この複合動作をsynchronizedまたはLockで原子操作に変えることができます。
例:
これ以外にも、java.util.co ncurrent.atomicの原子変数類を使って、カウンタ状態へのアクセスはすべて原子であることを確認できます。
例:
マルチスレッドがある変数にアクセスすると、この変数に対してスレッドが変更され、他のスレッドがすぐに最新の修正後の変数に読み込むことができます。
i+++はスレッド1(CPU 1)で演算が完了しているため、すぐにメインメモリに更新されていないが、スレッド2(CPU 2)はメインメモリの中で読み取り、プリントしているのが0である。
synchronizedとロックは視認性を保証することができます。
また、volatileキーワードでもこの問題を解決できます。
三、秩序性
プロセッサがより良い演算効率を持つために、自動的に最適化され、順序付けが私たちが書いたコードを実行することは知っていますが、実行結果は変わらないことを保証します。
例:
シングルパスでは問題ないですが、マルチスレッドでは問題が発生しやすいです。
もう一つの例を示します
しかし、この時点ではマルチスレッドの環境にあり、プロセッサが本当にこの二つのコードを並べ替えていると、問題が発生します。スレッド1が先にinited=trueを実行すると、この時、init()は実行されていません。スレッド2は既にwork()メソッドを起動しています。この場合、いくつかのクラッシュや他のBUGが発生する可能性があります。
synchronizedとロックは原子性を確保し、マルチスレッドでコードを実行させる際に順次実行されるので、自然と秩序があります。
volatileキーワードもこの問題を解決できます。volatileキーワードは規則性を保証し、プロセッサにこの行のコードを最適化しないようにします。
原子、もう一つの分割してはいけない粒子。原子性とは、もう一つ以上は分割できない操作のことです。
int i=1;//原子操作
i+;////原子以外の操作では、メインメモリからiをスレッドワークメモリに読み取り、+1を行い、更にiを朱メモリに書き込みます。
読み取りと書き込みは原子操作ですが、合わせて原子操作ではなく、「複合作業」とも言います。
この複合動作をsynchronizedまたはLockで原子操作に変えることができます。
例:
private synchronized void increase(){
i++;
}
または
private int i = 0;
Lock mLock = new ReentrantLock();
private void increase() {
mLock.lock();
try {
i++;
} finally{
mLock.unlock();
}
}
このように私達はこの一つの方法を一つの全体と見なし、一つの不可分な全体と見なすことができる。これ以外にも、java.util.co ncurrent.atomicの原子変数類を使って、カウンタ状態へのアクセスはすべて原子であることを確認できます。
例:
AtomicInteger mAtomicInteger = new AtomicInteger(0);
private void increase(){
mAtomicInteger.incrementAndGet();
}
二、可視性マルチスレッドがある変数にアクセスすると、この変数に対してスレッドが変更され、他のスレッドがすぐに最新の修正後の変数に読み込むことができます。
int i = 0;
// 1
i++;
// 2
System.out.print("i=" + i);
スレッド内のi++を実行してからスレッド2を実行しても、スレッド2の入力結果は2つの場合があります。一つは0と1です。i+++はスレッド1(CPU 1)で演算が完了しているため、すぐにメインメモリに更新されていないが、スレッド2(CPU 2)はメインメモリの中で読み取り、プリントしているのが0である。
synchronizedとロックは視認性を保証することができます。
また、volatileキーワードでもこの問題を解決できます。
三、秩序性
プロセッサがより良い演算効率を持つために、自動的に最適化され、順序付けが私たちが書いたコードを実行することは知っていますが、実行結果は変わらないことを保証します。
例:
int a = 0; // 1
int b = 0; // 2
i++; // 3
b++; // 4
このコードの実行順序は上の1、2、3、4の順に行われないかもしれません。1と2のデータ依存性がないので、3と4のデータ依存度がないです。2、1、4、3のように実行してもいいですか?全く大丈夫です。プロセッサは自動的に並べ替えてくれます。シングルパスでは問題ないですが、マルチスレッドでは問題が発生しやすいです。
もう一つの例を示します
// 1
init();
inited = true;
// 2
while(inited){
work();
}
init()inited=trueとデータに依存していません。一方通行では、二つのコードを置き換えると問題がないようです。しかし、この時点ではマルチスレッドの環境にあり、プロセッサが本当にこの二つのコードを並べ替えていると、問題が発生します。スレッド1が先にinited=trueを実行すると、この時、init()は実行されていません。スレッド2は既にwork()メソッドを起動しています。この場合、いくつかのクラッシュや他のBUGが発生する可能性があります。
synchronizedとロックは原子性を確保し、マルチスレッドでコードを実行させる際に順次実行されるので、自然と秩序があります。
volatileキーワードもこの問題を解決できます。volatileキーワードは規則性を保証し、プロセッサにこの行のコードを最適化しないようにします。