Java volatileキーワードの役割

3424 ワード

共有変数がvolatileで修飾されると、修正された値が直ちにホストメモリに更新されることを保証します.ここでの「保証」はどのようにして行われますか.JITの具体的なコンパイル後のCPU命令に関連していますか.
volatile特性
メモリの可視性:一般的には、スレッドAがvolatile変数に対する変更は、他のスレッドにとって表示されます.すなわち、スレッドがvolatile変数の値を取得するたびに最新です.
volatileの使用シーン
キーワードsychronizeを使用すると、複数のスレッドが同じコードに入ることを防止できます.特定のシーンでは、volatileはスレッドのコンテキスト切り替えを引き起こすことはありませんが、volatileを使用するには2つの条件を満たす必要があります.
1、変数の書き込み操作は現在の値に依存しない.例えば、マルチスレッドの下でa++を実行すると、volatileで結果の正確性を保証できない.
2、この変数は他の変数を持つ不変式に含まれていない.この文は少し拗ねていて、コードを見ると直感的だ.
public class NumberRange {
  private volatile int lower = 0;

  private volatile int upper = 10;

  public int getLower() { return lower; }

  public int getUpper() { return upper; }

  public void setLower(int value) {

     if (value> upper)

     throw new IllegalArgumentException(...);

     lower = value;

  }

  public void setUpper(int value) {

     if (value < lower)

     throw new IllegalArgumentException(...);

     upper = value;

   }
}

上記のコードでは、上下境界初期化はそれぞれ0と10であり、スレッドAとBがある時点でsetLower(8)とsetUpper(5)を同時に実行し、いずれも不変式のチェックに合格して無効範囲(8,5)を設定していると仮定するので、このようなシーンではsychronize保証方法setLowerとsetUpperで各時点で1つのスレッドしか実行できない必要がある.
上記でvolatileの役割を理解していない場合は、次の例を見てvolatileの実際の役割を見ることができます.
次は、プロジェクトでよく使用されるvolatileキーワードの2つのシーンです.
1、状態マーク量
高同時シーンでは、booleanタイプの変数isopenによって、制御コードが販促ロジックを実行するかどうか、どのように実現しますか?
public class ServerHandler {

      private volatile isopen;

      public void run() {
         
        if (isopen) {

            //    
              
        } else {

           //    
              
        }
          
    }

    public void setIsopen(boolean isopen) {

       this.isopen = isopen
          
    }      
}

上記の簡単な例では,現実のシーンでユーザがマルチスレッドにおけるrun()メソッドを実行し,販促ロジックを開く必要がある場合,setisopen(true)メソッドをバックグラウンドで設定するだけで,マルチスレッドにおけるメソッド制御の問題をうまく制御できることが明らかになった.この再生説明volatileキーワードの役割は、実行方法に最新の変数値を常に取得することを示すことです.
メモリの可視性を保証する方法
Java仮想マシンのメモリモデルでは、メインメモリとワークメモリの概念があり、各スレッドはワークメモリに対応し、メインメモリのデータを共有します.次に、通常の変数とvolatile変数の操作の違いを見てみましょう.
1、普通変数の場合:読み込み操作は作業メモリのデータを優先的に読み込み、作業メモリに存在しない場合、メインメモリから作業メモリにデータをコピーする.書き込みはワークメモリのコピーデータのみを変更します.この場合、他のスレッドでは変数の最新値を読み取ることはできません.
2、volatile変数に対して、読み操作時にJMMは作業メモリの対応する値を無効にし、スレッドに主メモリからデータを読み取るように要求する.書き込み時にJMMがワークメモリに対応するデータをメインメモリにリフレッシュする場合、他のスレッドは変数の最新値を読み取ることができます.
 
volatile変数のメモリ可視性はメモリバリア(Memory Barrier)に基づいて実現され、メモリバリアとは何ですか?メモリバリアは、メモリフェンスとも呼ばれ、CPUコマンドです.プログラムの実行時に、実行性能を向上させるために、コンパイラとプロセッサは命令を並べ替え、JMMは異なるコンパイラとCPUで同じ結果を保証するために、特定のタイプのメモリバリアを挿入することによって、特定のタイプのコンパイラの並べ替えとプロセッサの並べ替えを禁止する.メモリバリアを挿入すると、コンパイラとCPUに、どのコマンドもこのMemory Barrierコマンドと並べ替えることはできません.
 
class Singleton {

      private volatile static Singleton instance;

      private int a;

      private int b;

      private int b;

      public static Singleton getInstance() {
          
        if (instance == null) {

              syschronized(Singleton.class) {
                  
                if (instance == null) {

                      a = 1; // 1
                      b = 2; // 2
                      instance = new Singleton(); // 3
                      c = a + b; // 4
                }
                  
            }
              
        }
 
        return instance;  
    } 
}

1、変数instanceにvolatile修飾がない場合、文1、2、3は任意に並べ替え実行を行うことができ、すなわち命令実行プロセスは3214または1324である可能性がある.
2、volatile修飾変数instanceの場合、文3の前後にメモリバリアが1つずつ挿入されます.