Javaマルチスレッドのvolatileキーワード

2620 ワード

synchronizedキーワードと比較すると、volatileはより軽量レベルであり、スレッドコンテキストのスケジューリングと切り替えを引き起こさないため、使用コストも低い.Javaはvolatileの定義:
Javaプログラミング言語では、スレッドが共有変数にアクセスできるようにします.共有変数が正確かつ一貫して更新されることを保証するために、スレッドは排他ロックによってこの変数を個別に取得することを確保する必要があります.
簡単な理解は、1つの変数がvolatileキーワードで修飾されている場合、Javaはすべての変数が見ているこの変数の値が正しいことを確保することができます.この点を理解するには、いくつかの前置き知識が必要です.
オペレーティングシステムレベル
マルチスレッドの場合、複数のスレッドが書き込み操作を行う際に、まずマスタからCPUキャッシュに1部ずつコピーして実行し、実行後にキャッシュに書き込み、最後にマスタメモリにリフレッシュすることで、以前のスレッドの値を上書きするスレッドがあるというキャッシュ整合性の問題があります.この問題を解決するには、2つの方法があります.
  • バスを介してLOCKロックを追加する.
  • キャッシュコンシステンシプロトコルにより解決する.
  • まず第1の方式は他のCPUをブロックして、効率があまりにも低いので、第2の方式を採用して、核心思想は:操作の変数が無効であることを発見したら、他のCPUにその変数のキャッシュ行が無効であることを通知して、このように他のCPUはこの変数を読み取る時、再びメインメモリからデータをロードします
    Javaメモリモデル
  • 原子性マルチスレッド環境において、Javaは基本データ型の変数の宣言と付与に原子性があることを保証するだけである.そうでなければsynchronizedによって確保する必要があり、volatileは複合操作原子性を確保できない.
  • 可視性1つの変数がvolatileで修飾された後、スレッドのローカルメモリが無効であることを示し、1つのスレッドが共有変数を変更するとすぐにプライマリメモリに更新され、他のスレッドが共有変数を読み出すと、プライマリメモリから直接読み出す.もちろんsynchronizeとロックは可視性を保証する.
  • 秩序性Javaメモリモデルでは、コンパイラとプロセッサが命令を再ソートできるように効率的にするため、もちろん再ソートは単一スレッドの実行結果に影響しませんが、マルチスレッドに影響します.Javaはvolatileを提供して一定の秩序性を保証します.
  • volatileの原理
    volatileはスレッドの可視性を保証し,一定の秩序性を提供することができるが,原子性を保証することはできない.JVMの最下層volatileでは「メモリバリア」を採用しています.
    上の言葉には二つの意味がある.
  • 可視性、原子性を保証しない
  • 禁止命令並び替え
  • 1つ目の意味は前に紹介されていますが、2つ目の意味については以下のように説明されています.
  • プログラムがvolatile変数の読み取り操作または書き込み操作を実行すると、その前の操作の変更はすべて行われたことが肯定的であり、結果として後の操作が明らかになり、その後の操作は
  • まだ行われていないことが肯定的である.
  • 命令最適化を行う場合、volatile変数にアクセスする文をその後ろに置いて実行することも、volatile変数の後ろにある文を前にして実行することもできない.
    その実現原理について:volatileキーワードを追加すると、lockコマンドが1つ増え、メモリバリアに相当し、3つの機能を提供します.
  • は、命令の並べ替え時にその後ろの命令をメモリバリアの前の位置に並べないことを確保し、前の命令をメモリバリアの後ろの
  • に並べないことを確保する.
  • は、キャッシュに対する変更操作を直ちにプライマリメモリ
  • に書き込むように強制する.
  • 書き込みの場合、他のCPUで対応するキャッシュ行が無効になる
  • .
    シーンを使用:
  • ステータスフラグ
  • volatile boolean shutdownRequested;
     
    ...
     
    public void shutdown() { shutdownRequested = true; }
     
    public void doWork() { 
        while (!shutdownRequested) { 
            // do stuff
        }
    }
    
    
  • 使い捨て安全発表
  •   public class BackgroundFloobleLoader {
        public volatile Flooble theFlooble;
     
        public void initInBackground() {
            // do lots of stuff
            theFlooble = new Flooble();  // this is the only write to theFlooble
        }
    }
     
    public class SomeOtherClass {
        public void doWork() {
            while (true) { 
                // do some stuff...
                // use the Flooble, but only if it is ready
                if (floobleLoader.theFlooble != null) 
                    doSomething(floobleLoader.theFlooble);
            }
        }
    }