プログラムを実行する際、パフォーマンスを向上させるために、プロセッサーとコンパイラーは命令の順序を変更することがよくありますが、それは希望通りに並べ替えることはできません:
1。 . シングルスレッド環境ではプログラムの実行結果を変更することはできません
2. データの依存関係がある場合、並べ替えは許可されません
LZ の以前のブログを読んだことがある方なら、実際にはこれら 2 つの点がわかると思います。ある点に帰することができます: 渡すことはできません 前に発生する原則から派生した JMM では、任意の順序付けが可能です。
as-if-serial セマンティクスは、最適化のためにすべての操作を並べ替えることができますが、並べ替えの結果が変更できないことをコンパイラとランタイムとプロセッサの両方で保証する必要があることを意味します。 as-if-serial セマンティクスに準拠します。 as-if-serial はシングルスレッド環境のみを保証し、マルチスレッド環境では無効であることに注意してください。
簡単な例を使って説明しましょう:
int a = 1 ; //A int b = 2 ; //B int c = a + b; //C
A、B、C の 3 つの操作には次の関係があります。A と B にはデータ依存関係がありません。A と C、B と C にはデータ依存関係があります。並べ替えでは、A と B は任意にソートできますが、それらは C の前になければなりません。実行順序は A -> B -> C または B -> C になります。ただし、実行順序に関係なく、最終結果 C は常に 3 と等しくなります。
as-if-serail セマンティクスは、シングルスレッド プログラムを保護し、並べ替えを前提としてプログラムの最終結果が常に一貫していることを保証します。
実際、上記のコードでは、次のような前発生関係があります:
A 前に発生 B
B 前に発生 C
A 前に発生 C
1、2はプログラム順序規則、3は推移性です。しかし、並べ替えによって、B が A より前に実行される可能性があるということは意味しません。なぜ A が B より前に発生するのでしょうか?ここでも、A が B より前に発生するということは、A が必ず B より前に実行されるということではなく、A が B に見えるということですが、このプログラムに関しては、A の実行結果が B に見える必要はありません。したがって、JMM はこの並べ替えを違法とはみなしません。
これを理解する必要があります。プログラムの実行結果を変えずに、プログラムの動作効率を可能な限り向上させることです。
今、興味深いコードを見ていきます:
public class RecordExample1 { public static void main(String[] args){ int a = 1; int b = 2; try { a = 3; //A b = 1 / 0; //B } catch (Exception e) { } finally { System.out.println("a = " + a); } } }
並べ替えルールに従って、操作 A と操作 B が並べ替えられると、この時点で B は例外をスローします (/ ゼロ)。ステートメント A は確実に実行されないので、a は 3 に等しいでしょうか? as-if-serial の原則に従うと、プログラムの結果が変わります。実際、JVM は、as-if-serial セマンティクスを保証するために、例外に対して特別な処理を実行します。JIT は、並べ替え中に catch ステートメントにエラー補正を挿入します。 = 3)、そうすることで cathc のロジックが複雑になりますが、JIT 最適化の原則は、catch ブロックのロジックの複雑さを犠牲にしても、プログラムの通常の動作の下で可能な限りロジックを最適化することです。
シングルスレッド環境では、as-if-serial セマンティクスにより、並べ替えが最終結果に影響を与えることはありませんが、マルチスレッド環境ではどうなるでしょうか?
次のコード (volatile の古典的な使用法):
public class RecordExample2 { int a = 0; boolean flag = false; /** * A线程执行 */ public void writer(){ a = 1; // 1 flag = true; // 2 } /** * B线程执行 */ public void read(){ if(flag){ // 3 int i = a + a; // 4 } } }
スレッド A は Writer() を実行し、スレッド B は read() を実行します。スレッド B は実行中に a = 1 を読み取ることができますか?答えは必ずしもそうではありません (注: X86 CPU は書き込み-書き込み並べ替えをサポートしていません。x86 で動作させる場合、これは間違いなく a=1 になります。LZ は長い間テストしていませんでしたが、確認した後に最終的に判明しました)情報)。
操作1と操作2の間にはデータの依存関係がないため、並べ替えが可能です。操作3と操作4の間にはデータの依存関係はありません。並べ替えも可能ですが、操作3と操作4の間には制御の依存関係があります。 。操作 1 と 2 が並べ替えられた場合:
この実行順序によれば、スレッド B はスレッド A によって設定された値を読み取ることができなくなります。ここでのマルチスレッドのセマンティクスは並べ替えによって破壊されました。
操作 3 と操作 4 は並べ替えることもできますが、ここでは説明しません。ただし、操作 3 が成立した場合にのみ操作 4 が実行されるため、これらの間には制御依存関係があります。コード内に制御の依存関係が存在すると、命令シーケンスの実行の並列性に影響を与えるため、コンパイラとプロセッサは、推測実行を使用して制御の依存関係が並列処理に及ぼす影響を克服します。操作3と操作4を並べ替えて、操作4を先に実行すると、操作3が真の場合、計算結果は変数i
に一時的に保存されます。上記の分析により、次のことがわかります。 並べ替えは、シングルスレッド環境での実行結果には影響しませんが、マルチスレッドの実行セマンティクスを破壊します。
上記は、[Java Concurrency]-----Java メモリ モデルの並べ替えの内容です。さらに関連する内容については、PHP 中国語 Web サイト (m.sbmmt.com) に注目してください。