まず、揮発性何ですか?
揮発性は、Java、Java仮想マシンが提供する軽量な同期メカニズムのキーワードです。
二、JMM(Javaのメモリモデル)
優れた揮発性のキーワードを理解するには、JMMを理解することを学ぶ必要があります。
JMM(Javaメモリー・モデルのJavaメモリ・モデル、JMMをいう)自体は抽象概念であり、それはインスタンス・フィールドを含む各変数の仕様(カスタマイズされた規則または手続きのセットを説明し、実際のない静的フィールドおよび標準化を構成します配列オブジェクトの要素)アクセス方法。
JMMの特徴:
図1に示すように、可視
2、アトミック
3、秩序
同期の要件についてJMM:
1は、スレッドのロックを解除する前に共有変数の値は、メインメモリに戻って更新する必要があります
彼らのワーキングメモリにメインメモリの最新の値を読み取る必要があり、スレッドをロックする前に、2
3、同じロックロックロック解除は、
(いくつかの場所でスタック空間になる)プログラムは、エンティティJVMスレッドを実行され、各スレッドは、JVMがワーキングメモリを作成します作成されているので、ワーキングメモリは、各スレッドのプライベートデータ領域であり、Javaのメモリモデルの下ですべての変数メインメモリに格納され、メインメモリは、すべてのスレッドがアクセス可能なメモリ領域を、共有されている、しかし、変数(代入を読んで、など)上のスレッド操作はワーキングメモリで実行されなければならない、自分の仕事にメインメモリからコピーにしたい最初の変数空間、次いで変数の操作、可変動作が完了し、その後、メインメモリに書き戻す、メインメモリに直接変数を操作することができない、ワーキングメモリストア内の各スレッドは、メインメモリ内の変数のコピーのコピーなので、異なるスレッドがお互いにアクセスすることはできません(値によって)ワーキングメモリ、通信は、以下にアクセス手順について簡単にケースの間にメインメモリによって行われなければなりません。
三、volatileキーワード三つの特徴
3.1視認性を確保
JMMの先に記述することで、我々は、メインメモリへの各スレッド操作変数は、個々のスレッドに各オペレーティング彼らのワーキングメモリをコピーして、メインメモリにライトバックされて共有されていることを知っています。この時間は、スレッドAが戻ってメインメモリに書き込まれていない共有変数Xの値を変更があってもよい、別のスレッドBが動作変数Xの共有メモリを有するが、ワーキングメモリで、この時間Aスレッドは、変数を共有しましたXは、Bをスレッドに可視メインメモリ同期遅延と、このワーキングメモリと可視性の問題を引き起こすありません。
3.1.1コードは、揮発性のキーワードを追加しない→示しています。
1 クラスのMyData { 2 。3 INT番号= 0; 4 。5 公共 ボイド追加(){ 6 本 .NUMBER = 60; 7 } 。8 } 。9 10 //速やかに他のスレッドは、一次物理メモリの値を通知し、視認性を確保する揮発性改変されている 11。 プライベート 静的 ボイドメイン(文字列[]引数)を、{ 12は MYDATA MYDATA = 新しい新しい MYDATA(); 13は 14 の新しい新しいスレッド(() - > { 15 。のSystem.out.println(にThread.currentThread()のgetName() + " に来て)"; 16 17 トライ { 18で TimeUnit.SECONDS.sleep(3); 19 } キャッチ(InterruptedExceptionあるE){ 20は e.printStackTrace(); 21である } 22である 23である myData.add()は、 24 のSystem.out.println(にThread.currentThread() .getName()+ " 更新に値 " + myData.number); 25 }、 " スレッドA ").start(); 26である 27 ながら(myData.number == 0){ 28 //メインスレッドは、サイクルを待たなければなりません数の値は、もはや0でなくなるまで 29 } 30 System.out.println(にThread.currentThread()のgetName()+ " 使命が終わって、メインGET値 " + myData.number)。 31 }
スレッドが開始された後、最初に印刷します「Aのスレッドが来る」、変数の数は60で、変更、3秒待って、スレッドAが開始されたことを示しています。しかし、プログラムが実行されています。番号を取得するには、メインスレッドの値がゼロであるので、私は、可変数の値が60に変更されているので、メインスレッドがループで待機していたかわかりません。どの変数番号の値は持っていないとき、それは、見ることができ、視認性を。
3.1.2コードは、volatileキーワードを追加→示しています。
上記のコードが、あなたは、変数の数を宣言する場合、揮発性のキーワードを追加します。
スレッドAは、可変数は60である変性後、メインスレッドは、「メインミッションが終わった、主が値60を取得する」コンソールにプリントアウト、ループのうち、直ちに通知されています。
3.2原子性を保証するものではありません。
3.2.1コードが実証:(実行の開口20件のスレッド1000から操作変数の数を増加させます)
1 クラスのMyData { 2 3 揮発性 のint番号= 0。 4 5 公共 ボイド addPlusPlus(){ 6 番号++。 7 } 8 } 9 10 パブリック 静的 ボイドメイン(文字列[]引数){ 11 のMyData MYDATA = 新規のMyData()。 12 13 のために(INT I 1 =、iが<= 20; I ++){ 14 新しいスレッド(() - > { 15 のために(INT J = 1; J <= 1000; J ++){ 16 myData.addPlusPlus()。 17 } 18 }、String.valueOf(I))(開始)。 19 } 20 21 ながら(Thread.activeCount()> 2){ 22 Thread.yield()。 23 } 24 25 のSystem.out.println(にThread.currentThread()のgetName()+ " 最終的に数値は + myData.number")。 26 }
テスト結果が20,000でない、各実行結果は同じではありません。
3.2.2分析:
3.2.3ソリューション:
- この方法でsynchronizedキーワードを追加
- パケットjava.util.concurrnent.atomicで使用されるのAtomicInteger
1 class MyData { 2 AtomicInteger atomicInteger = new AtomicInteger(); 3 public void addMyAtomic() { 4 atomicInteger.getAndIncrement(); 5 } 6 }
3.3 禁止指令重排
计算机在执行程序时,为了提高性能,编译器和处理器常常会做指令重排,一把分为以下3种:
- 编译器优化的重排
- 指令并行的重排
- 内存系统的重排
单线程环境里面确保程序最终执行结果和代码顺序执行的结果一致。处理器在进行重新排序是必须要考虑指令之间的数据依赖性。多线程环境中线程交替执行,由于编译器优化重排的存在,两个线程使用的变量能否保持一致性是无法确定的,结果无法预测。
四、volatile 关键字的使用
1 /** 2 * 双重检查机制的单例模式 3 */ 4 public class SingletonDemo { 5 6 private static volatile SingletonDemo instance = null; 7 8 private SingletonDemo() { 9 System.out.println(Thread.currentThread().getName() + " 构造方法"); 10 } 11 12 public static SingletonDemo getInstance() { 13 if (instance == null) { 14 synchronized (SingletonDemo.class) { 15 if (instance == null) { 16 instance = new SingletonDemo(); 17 } 18 } 19 } 20 return instance; 21 } 22 23 public static void main(String[] args) { 24 for (int i = 0; i < 10; i++) { 25 new Thread(SingletonDemo::getInstance).start(); 26 } 27 } 28 } 29