Javaのマルチスレッド共有可変制御
- 可視
変数の共通の価値観への1つのスレッドの変更は、他のスレッドがタイムリーに見ることができるならば、それは共有変数の可視性と呼ばれます。変数のコピーが複数のスレッドでワーキングメモリに存在する場合、この変数は共有変数と呼ばれています
- JMM(Javaのメモリモデル)
共有メインメモリ変数に同時に複数のスレッドを読んで、変更する場合は、変更を行うために、このコピーの後にコピーとして彼らのワーキングメモリにこの変数を読み込み、その後、戻ってどこ変数メインメモリへの更新します場所。
(CPUタイムスライスは、スレッドの最小単位であるので、ので、ここでワーキングメモリが実際に物理的キャッシュ、ローカルデータ取得CPUの動作を指し、また、メインメモリを参照すると、原稿を格納する変数を共有されるメモリであります場所)
二つの条項:
。すべての操作のための共有変数をスレッドあなたが直接メインメモリを動作させることができない、ワーキングメモリに行われなければなりません
B。異なるスレッド間のスレッド間で互いのワーキングメモリの変数にアクセスすることはできませんメインメモリを通過する変数の値を渡します
あなたがスレッド2に共有変数xスレッド1を変更した場合表示され、その後、以下の手順を実行する必要があります。
。Xの値は、メイン・メモリ・スレッド1を更新するために変更されます
B。メインメモリは、スレッド2 xの更新メモリの作業コピーにxの値を更新しています
したがって、共有変数の可視性を達成するために以下の2点を確認する必要があります。
メインメモリに変更してタイムリーなアップデートの作業の。スレッド・メモリコピー
B。他のスレッドはその変数の独自のメモリの作業コピーを更新するために、メインメモリリフレッシュ上の共有変数を促すことができます
Javaは、同期共有変数、揮発性、javaの同時クラスの可視性によって達成することができます
- 可視性を実現する同期
コードブロックは、実際に同期されている変更共有変数が同期コードブロックにアクセスするために複数のスレッドをアクセスするためのミューテックスをインクリメントされ、コードブロックの内容にアクセスして修正するための時間に1つだけのスレッドが(ロック)があり、このスレッドを待っている他のすべてのスレッドがコードブロック(ロック解除)を残すようにするとき、同期コードブロックを入力する機会を持っています。
次のようにそのため、同期コード・ブロックへのスレッドの前と後、処理が行われます。
ミューテックスを取得する。スレッド
B。ワーキングメモリをクリア
C。ワーキングメモリ変数のレプリカに、メインメモリの共有値の最新のコピーから、
D。コードが実行されます
すなわち、メインメモリに戻って修正リフレッシュの値のコピー
F。スレッドがロックを解除します
その後、共有変数の値が、同期コードブロックに入る他のコードは、メモリは、スレッドの変更の最新の値で動作するように読まれます。
クロススレッド実行ので、コードブロック(修飾されたアクセスは、変数を共有)との間で共有複数のスレッドを実行し、共有変数最終結果の最後の値は、複数を有することができます。
public class SynchronizedTest {
private boolean ready = false; private int result = 0; private int number = 1; public void write(){ ready = true; number = 2; } public void read(){if(ready){ result = number * 3; } System.out.println("result is " + result); } private class TestThread extends Thread{ private boolean flag; public TestThread(boolean flag){ this.flag = flag; } @Override public void run() { // TODO Auto-generated method stub if(flag){ write(); }else{ read(); } } } public static void main(String[] args){ SynchronizedTest test = new SynchronizedTest(); test.new TestThread(true).start(); test.new TestThread(false).start(); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
コードとして、2つの実行スレッドが交差するので、最終的な結果は、0又は3又は6の結果であり得ます
共有変数は、主に次の理由で表示されません。
。クロススレッドの実行
B。並べ替え
共有変数を更新するために、C。失敗
同期用いて同期キーワードに変更され、コード及び可視メソッドを読み書きするために加え(全て終了で実行さをことを確実にするために、同期またはしないコンテンツの実行コードブロック)アトミック性を保証します
- 揮発性は、(JDK 1.5の後)の可視性を実現します
どのように揮発性の可視性を実現しますか?
揮発性変数は、スレッドは、メインメモリからの変数の最新値を再読み込みすることを余儀なくされ、変数の変更が発生変更するとき、スレッドは、バックメインメモリに最新の値を更新するように強制される、たびにスレッドにアクセスされます。その結果、異なるスレッドは、タイムリーな変数の最新の値を見ることができます。
しかし、我々は、アトミック揮発性変数の変更を保証することはできません。
このような数++として、この操作は3つのオペレーション(数、数プラス1、新しい価値のライトバックナンバーを読む)のコレクションが実際にある、揮発性の唯一の保証は、すべてのスレッドの操作のすべてのステップ表示されますが、もし2操作の6セットの合計が、実行の間に交差する可能性があるようにスレッドは、++数を実行する必要があり、その後、最終的な数は、結果は期待できない可能性があります。
だから、この数++非アトミック操作のために、同期推奨:
synchronized(this){
number++;
}
- 1
- 2
- 3
以下のコード:最終的な結果は必ずしも番号500ないが、数は視認性が揮発性で保証することができない、アトミック操作を++ではないので、500未満であってもよいです
public class VolatileTest {
public static int number = 0; public void increase(){ try { Thread.sleep(300); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } number++; } /** * @param args */ public static void main(String[] args) { final VolatileTest test = new VolatileTest(); for(int i = 0 ; i < 500 ; i++){ new Thread(new Runnable() { @Override public void run() { test.increase(); } }).start(); } //若当期依然有子线程没有执行完毕 while(Thread.activeCount() > 1){ Thread.yield();//使得当前线程(主线程)让出CPU时间片 } System.out.println("number is " + number); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
非アトミックインクリメント操作等のために、によって視認性を確保することができます。
A。同期
B。ReentrantLockの
C。AtomicInteger
次のように同期が改正しました:
public void increase(){
try {
Thread.sleep(300); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } synchronized(this){ number++; } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
次のようにReentrantLockのは、変更します:
public class VolatileTest {
public static int number = 0; public Lock lock = new ReentrantLock(); public void increase(){ try { Thread.sleep(300); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } lock.lock(); try{ number++;//这块的代码实际项目中可能会出现异常,所以要捕获 }finally{ lock.unlock();//用try finally块保证Unlock一定要执行 } } 。。。 }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
AtomicInteger、アトミック操作は、クラス整数を提供します。Java言語では、++ iと私は必然的にsynchronizedキーワードを使用しますが、使用時の動作は、スレッドセーフではありません++します。AtomicIntegerは、スレッドセーフ減算オペレータインターフェースによるものです。
次のように改正:
package com.mooc.test;
import java.util.concurrent.atomic.AtomicInteger;
public class VolatileTest { public static AtomicInteger number = new AtomicInteger(0); public void increase(){ try { Thread.sleep(300); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } number.getAndIncrement();//获得当前值并且加1 } /** * @param args */ public static void main(String[] args) { final VolatileTest test = new VolatileTest(); for(int i = 0 ; i < 500 ; i++){ new Thread(new Runnable() { @Override public void run() { test.increase(); } }).start(); } //若当期依然有子线程没有执行完毕 while(Thread.activeCount() > 1){ Thread.yield();//使得当前线程(主线程)让出CPU时间片 } System.out.println("number is " + number.get()); } }
1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 該当する揮発性
。書き込み動作は、変数の現在の値に依存しません
そのようなインクリメントデクリメント、数=数+ 5、等(満たされていません)
B。現在の揮発性変数は、他の揮発性の変数に依存しません
例えばvolatile_var> volatile_var2この不等式(満たされていません)
- 比較同期と揮発性
。揮発同期動作を必要としない、より効率的であり、スレッドをブロックしないが、比較的狭いの応用
(即ち、同期コードブロックに)ロックに対応するB。揮発性読み出し変数、及び解除(出口同期コード・ブロック)に対応する変数を書きます
揮発性視認性のみ保証することができ; Cは、視認性を確保することができ、ロックアトミック操作内に固定することができる共有変数を同期