Javaのメモリモデルの深い理解(3) - シーケンシャル一貫性

データの競争との整合性を確保するために

プログラムが正常に同期されていない場合は、データが競争を存在します。次のようにJavaのメモリモデルの仕様は、競合データを定義します。

  • 1つのスレッドで変数を書きます、
  • 同じ変数を読み込み、別のスレッドで、
  • 読み取り、書き込み、スルーソートするために同期されません。

コードは、データ競合が含まれている場合、プログラムの実行は、直観結果を(例では、前の章のケースである)を生成する傾向があります。マルチスレッド化されたプログラムが正常に同期させることができた場合は、このプログラムは、競争力のあるプロセスデータではありません。

JMMメモリの一貫性が正しく同期プログラムは、以下の保証をしたマルチスレッド:

  • プログラムが正常に同期されている場合は、実行は、順次一貫性(逐次一貫性)があります - 、我々はすぐに見るであろうと同じ結果である(シーケンシャル一貫性メモリモデルのプログラムのプログラムの実行結果であるためプログラマは)強力な保証です。ここでの同期化は、(ロック、揮発性および最終的な)共通の同期プリミティブの正しい使用を含む、広義の同期を指します。

シーケンシャル一貫性メモリモデル

シーケンシャル一貫性メモリモデルは、メモリ・プログラマの可視性のための強力な保証を提供して理想化された理論計算機科学者の参照モデル、です。シーケンシャル一貫性メモリモデルは、2つの主要な機能を備えています。

  • すべての操作は、プログラム順に実行スレッドでなければなりません。
  • (かどうかに関係なく、プログラムの同期の)すべてのスレッドのみが実行される動作の単一のシーケンスを見ることができます。シーケンシャル一貫性メモリモデルでは、各動作が実行されなければならず、すべてのスレッドに原子直ちに目に見えます。

以下を表示するために、プログラマが提供するシーケンシャル一貫性メモリモデル:

概念的には、シーケンシャル一貫性モデルは、このメモリの周りに揺動することにより、スレッドスイッチのいずれかに接続することができ、単一のグローバルメモリを有しています。同時に、各スレッドがメモリの読み取り/書き込み操作を実行するためにプログラムされなければなりません。グラフから、我々は、ほとんどの任意の時点で一つだけのスレッドがメモリに接続することができることがわかります。同時実行複数のスレッド、図のスイッチング素子は、すべてのスレッドがすべてのメモリを読み取ることができたときに/書き込み操作をシリアライズ。

よりよく理解するために、2つの図で、順次一貫性モデルの特性のさらなる説明を見てみましょう。

AとBが同時に実行される2つのスレッドが存在すると仮定する。スレッドには3つの操作を有し、プログラム内の順序は以下のとおりです。A1-> A2-> A3。スレッドBは、3つの操作を持って、プログラム内の順序は以下のとおりです。B1-> B2-> B3。

2つのスレッドが正しい同期を監視するために使用されると仮定する:スレッドは、3つの操作を行った後、モニタを解放し、同じモニタを獲得するためにBのスレッド。下に示すように、順次一貫性モデルにおけるプログラムの実行結果:

今、私たちは2つのスレッドが同期しないことを前提とし、以下では、このプログラムの実装の概略図は、順次一貫性モデルを同期化されていないです。

シーケンシャル一貫性モデルにおけるNO同期プログラムは、ものの、全体的な実行順序は順不同ではありませんが、すべてのスレッドが唯一の一貫した総合的な実行順序を見ることができます。B1-> A1-> A2-> B2-> A3-> B3:例えば上記図は、実行の順序である見るためにAとBをスレッド。私たちは、各操作がどのスレッドにすぐに表示されなければなりませんシーケンシャル一貫性メモリモデルので、この保証を取得することができました。

しかし、JMMは、この保証はありません。JMMは、同期プログラムではないだけでなく、オーダーの全体的な実装が故障しているが、順序スレッドはすべてのアクションにも一致しないことが見ている中で。例えば、現在のスレッドに書き込まれたデータは、ローカルメモリにキャッシュされ、メインメモリの前にリフレッシュされていない、書き込み動作は、現在のスレッドに表示され、他のスレッドの観点から見て、これは単純に書き込みしていないと思います現在のスレッドの実行。メインメモリに書き込まれたローカルメモリデータにのみ、現在のスレッドのリフレッシュ後、書き込み動作は、他のスレッドに見えるようにします。この場合、現在のスレッドと他のスレッドが動作を順次矛盾行わ見ています。

シーケンシャル一貫性同期プログラムの効果

ここでは、適切に、順次一貫性を持つプログラムを同期する方法については、前の例のプログラムと同期するように監視ReorderExample。

次のサンプルコードを考えてみましょう。

class SynchronizedExample {
int a = 0;
boolean flag = false;

public synchronized void writer() {
    a = 1;
    flag = true;
}

public synchronized void reader() {
    if (flag) {
        int i = a;
        ……
    }
}
}

実行のスレッド、上記サンプルコードの後、ライター()メソッド、Bのスレッド実行リーダー()メソッドを想定。これは、適切に同期マルチスレッド化されたプログラムです。JMM仕様、プログラムの実行結果は、シーケンシャル一貫性モデルでプログラムを実行するのと同じ結果を有するであろう。以下は、2つのメモリモデルプログラムの実行タイミングの比較表のとおりです。

シーケンシャル一貫性モデルでは、プログラムの光で全ての操作は、シリアル順序で実行しました。JMMでは、クリティカル領域を並べ替えるコードは(しかし、モニターのセマンティクスを損なうこと、重要なゾーンの外に「エスケープ」コードJMMクリティカル領域を許可していません)することができます。JMMは、スレッドは、両方の時点で同じメモリ順次一貫性のあるビューを(詳細は本明細書に説明する)を有するように、監視モニター時間の二つの重要な点を入力し、出にいくつかの特別な処理を行います。臨界領域内のスレッドは、スレッドBは、重要な地域で並べ替えスレッドAに「観察」することができないの実装を、並べ替え、しかし性質による相互に排他的なモニターんが。この並べ替えは、効率が改善されますが、プログラムの結果を変更していないだけ。

ここから、私たちはの具体的な実現にJMMの基本方針を確認することができます。プログラムの実行結果の前提の下で(正しい同期)を変更せずに、可能な最適化コンパイラやプロセッサへの扉を開きます。

プログラムの実行特性は同期されません

マルチスレッドのプログラムが同期していないか、適切に同期されていないために、JMMは最小限のセキュリティを提供します。デフォルト値(0、NULL、false)のいずれかで、スレッドを書か前または値、スレッドの実行の値を読み取るために、読み取り値の読み取りはJMMが出てくる(薄い空気のうち)何を保証しないスレッドはありません。最低限のセキュリティを達成するために、第一のメモリ空間内のオブジェクトを割り当てJVMヒープがクリアされ、次いで(JVMは、2つの操作を同期する内部)上記目的を割り当てます。したがって、明確なメモリ空間(メモリを事前ゼロ)への割り当ては、ドメインのデフォルトの初期化が完了したとき。

JMMは、プログラムの実行結果は、順次一貫性モデルにおけるプログラムの実行結果と同期されていない保証するものではありません。シーケンシャル一貫性モデルにおけるプログラムの実行を同期していないので、全体的にその実行の混沌とし​​た、予期しない結果です。同じ意味の両方のモデルに同期されていないプログラムの結果の実装を確認してください。

全体的にJMMには同期手順は、乱れていないときや、シーケンシャル一貫性モデルは、その実行結果は予測できません。同時に、両方のモデルに同期していないプログラムの実行特性における次のような違いは以下のとおりです。

  1. JMMは、単一スレッド内保証動作をしないながらシングルスレッドプログラムの一貫性は、順次実行される保証するために動作するモデルは、プログラムの順序(例えば、正しくクリティカル領域を並べ替える同期上記マルチスレッドプログラムとして)実行されます。すでにここではそれらを繰り返さない、言及されています。
  2. JMMは、すべてのスレッドが動作実行順序の一貫性のあるビューを見ることを保証するものではありませんが、すべてのスレッドが唯一、同じ手順の操作を行って見ることができることを保証するために、順次一貫性モデル。この前も、ここではそれらを繰り返さない、について話されました。
  3. メモリの読み出し/書き込み操作のすべてがアトミックであることを保証するために、シーケンシャル一貫性モデルをJMMは64ビット型の長い読み取りを保証するものではありませんし、double型の変数/書き込み操作はアトミックです。

第三の違いは、プロセッサバスの作業機構に密接に関係しています。コンピュータでは、データは、バスを介してプロセッサとメモリとの間で転送します。プロセッサとメモリとの間の各データ転送は、一連のステップを介して達成される、手順のこのシリーズは、バス・トランザクション(バス・トランザクション)と呼ばれます。バス・トランザクションは、読み込みのトランザクション(読み取りトランザクション)と書き込みトランザクション(ライト・トランザクション)が含まれています。メモリは、プロセッサ、メモリから、プロセッサからの転送データへの書き込みトランザクションを転送するデータをトランザクションを読み取り、各トランザクションは、読み出し/書き込みメモリ、物理的に連続した1つまたは複数の単語です。ここで重要なのは、バスがバス・トランザクションの同時使用を同期しようとするということです。プロセッサの間にバストランザクションを実行し、バスは、メモリの読み出し/書き込みを行うために他のすべてのプロセッサおよびI / Oデバイスを禁止します。バスのメカニズムを説明するための模式図を通過させるの仕事:

同時に、プロセッサA、BおよびCを想定し、バス調停(バスアービトレーション)バスは、我々は、決定プロセッサバスAは、調停での競争に勝つことを前提とし、上記開始与党のバス・トランザクションを、(競合しますバス調停は)すべてのプロセッサが平等なアクセス・メモリを持っていることを保証します。他の二つのプロセッサがバス完了Aトランザクション・プロセッサ・メモリ・アクセスのために待機する必要がありますが、そのバストランザクションを続行するには、この時点でプロセッサが再び開始することができます。実行プロセッサバストランザクション中仮説(かかわらず、バストランザクションが読み出しトランザクションまたは書き込みトランザクションであるかどうか)、Dプロセッサは、バスに要求プロセッサバスDが禁止される今回のバス・トランザクションを開始しました。

バスの作業メカニズムは、実行のシリアルにアクセスメモリへのすべてのプロセッサを置くことができ、任意の時点で、ほとんど唯一つのプロセッサでメモリにアクセスすることができます。単一のトランザクション/ライト動作にメモリリードバスがアトミックであることをこの機能が保証されます。

64ビットのデータ上で所望の読み取り/書き込み操作がアトミックである場合、いくつかの32ビットプロセッサでは、コストが比較的大きくなります。Java言語仕様は奨励し、そのようなプロセッサの世話をするが、二重の長い変数と変数の読み取り/書き込み原子のJVM 64を強制しないために。JVMは、このプロセッサ上で実行されたとき、長い64ビットを入れ/二重変数は、読み取り/書き込み操作が行われる2つの32ビットの読み出し/書き込み動作に分割されます。これら2つの32ビットの読み出し/書き込み動作は、異なるバス・トランザクションが実行されるに割り当てることができ、64ビット変数に今回は、読み取り/書き込みがアトミックではありません。

メモリ操作は単一の原子意志を持っていない場合は可能性が予期しない結果をもたらします。次の図を考えてみます。

プロセッサBが長い変数を読み取るようにしながら、プロセッサと仮定すると、上記の長い変数Aを書き込みます。64ビットプロセッサの書き込み動作は2つの32ビット書き込み操作に分割され、そして2つの32ビット書き込みは異なる書き込みトランザクションが実行されるに割り当てられています。一方、プロセッサBの64ビットは、2つの32ビットの読み出し動作に分割を読み込み、2つの32ビットの読み出し動作が実行される同じリードトランザクションに割り当てられます。プロセッサA及びBが実行するタイミング図によれば、プロセッサは、プロセッサB A「半書き込まれた」無効な値が表示されます。

 

 

公開された136元の記事 ウォンの賞賛6 ビュー1489

おすすめ

転載: blog.csdn.net/weixin_42073629/article/details/104741472