揮発性の実装の原則に

同期ヘビー級のロックで、揮発性が軽量同期され、それはマルチスレッド開発における共有変数の「可視性」を保証します。変数が揮発使用している場合、それはスレッドコンテキストの切り替えやスケジューリングを起こさないので、それは同期の使用よりもはるかに低いコストで、です。

Javaプログラミング言語は、スレッドが正確かつ一貫して更新されたスレッドだけでは、この変数を介して取得する排他ロックを保証しなければならないことができる共有変数を確保するために、共有変数にアクセスすることができます。人気が揮発性の場合は変更されている変数を話し、それはすべてのJavaスレッドはこの変数の値を参照することを保証することは同じです。共有変数の揮発性修正のスレッドが更新される場合は、他のスレッドがただちにスレッ​​ドの可視性と呼ばれるアップデートを、見ることができます。

メモリ・モデルの概念

しかし、揮発性が理解する前に、我々はJavaのメモリモデルに関連する概念を理解する必要があり、Javaのメモリモデルと関連している、揮発性の理解することはまだ少し難しいです。

オペレーティングシステムのセマンティクス

プログラムを実行しているコンピュータは、各命令の実行中にCPUで実行されると、必然的にデータの読み書きを伴います。私たちは、データをメインメモリに実行中のプログラムが格納されていることを知って、その後、任意の相互作用が意志大幅にメインメモリに対処するために必要な場合は、命令を実行するための高速CPUではない読んで、メインメモリにデータを書き込む、問題があるでしょうCPUのキャッシュがあるので、効率に影響を与えます。唯一のスレッドがCPUで実行されていると、独特のためのCPUのCPUキャッシュ。

CPUキャッシュと効率の問題を解決し、それが新たな問題をもたらすでしょう:データの一貫性を。

プログラム動作では、コピー動作は実行の最後ならば、CPUが動作中にメインメモリを扱うことはできませんが、キャッシュから直接データを読み書きするために、CPUのキャッシュにデータが必要になりますこれは、メインメモリにデータをフラッシュします。

簡単な例を見てみましょう:

i = i + 1;
复制代码

スレッドがこのコードを実行する場合、それはメインメモリからI(この時点で、I = 1が仮定されている)の値を読み出し、その後、CPUにキャッシュをコピーし、CPUが実行する操作+ 1(ここで、i = 2)、そのデータは2は、キャッシュを伝え、そして最終的にはメインメモリにフラッシュ= Iに書き込まれます。

実際には、シングルスレッドでそうすることは問題ありません、複数のスレッドでの問題があります。次のように:

假如有两个线程 A、B 都执行这个操作( i++ ),
复制代码

私は、私たちの通常の論理的思考、メインメモリなければならない= 3に従って値。

しかし、実際には、これはそれですか?次のように:

两个线程从主存中读取 i 的值( 假设此时 i = 1 ),到各自的高速缓存中,
然后线程 A 执行 +1 操作并将结果写入高速缓存中,最后写入主存中,此时主存 i = 2 。
线程B做同样的操作,主存中的 i 仍然 =2 。所以最终结果为 2 并不是 3 。
这种现象就是缓存一致性问题。
复制代码

キャッシュ・コヒーレンスを解決するには、2つのソリューションがあります。

通过在总线加 LOCK# 锁的方式
通过缓存一致性协议
复制代码

問題がある最初のシナリオは、それが1つのだけCPUが他のCPUを実行することができ、すなわちバス-LOCKの#ロックを達成するための排他的な方法を採用することでブロックされていたより効率が低いです。

第2のオプションは、キャッシュ・コヒーレンス・プロトコル(MESIプロトコル)は、各キャッシュで使用される共有変数のコピーが一貫していることを保証します。次のように核となるアイデアは、次のとおりです。見つかった変数を読み取るときにCPUデータを書き込むときに見つかったオペレーティング変数が共有変数であるならば、それは変数の通知を他のCPUのキャッシュラインを通知しますが、無効なので、他のCPUである場合には無効は、メインメモリからデータをリロードします。

Javaのメモリモデル

上記のオペレーティング・システム・レベルでのは、Javaメモリモデル、それが私たちに保証を提供するもので、ちょっと見て見てみましょう、データの整合性を確保する方法を説明し、そしてそれは、私たちはより多く作るようにする、Javaのメソッドやメカニズムを提供しますスレッドプログラミングの実行の正しさを確認してください。

アトミック、可視性、秩序:並行プログラミングでは、我々は通常、三つの基本的な概念に遭遇します。私たちは、揮発見えます。

不可分性

アトミック:操作または動作、すなわち複数、いずれかのすべてが実行され、任意の因子の実行が中断されない、または実行できません。

アトミックデータベースのトランザクション内と同様に、我々は次のように簡単な例を見てみましょう。

i = 0;  // <1>
j = i ;  // <2>
i++;  // <3>
i = j + 1; // <4>
复制代码

少数ではないことを上記の4つの操作は、アトミックであるいくつかは、そこにありますか?かなり理解していない場合、あなたは思うかもしれアトミック操作され、実際には、唯一のアトミック操作で、残りはありませんでした。

  1. Javaでは、変数と代入演算の基本データ型は、アトミック操作です。
  2. 2つの操作を含んでいる:私はjに割り当てられた値である、読み。
  3. これは、3つの操作からなる:iの値を読み出し、I + 1は、I +1、結果に割り当てられます。
  4. <3>のように

そして、64ビットJDKの環境、それは原子であるかどうか、64ビットのデータを読み書きしますか?

实现对普通long与double的读写不要求是原子的(但如果实现为原子操作也OK)
实现对volatile long与volatile double的读写必须是原子的(没有选择余地)
复制代码

さらに、揮発性化合物は、原子操作を保証するものではありません

可視

視認性は、他のスレッドが直ちに変更された値を確認することができ、複数のスレッドが同じ変数にアクセスした場合、スレッドはこの変数の値を変更することを意味します。

上記では、他のスレッドが表示されていないために、共有変数をスレッドを動作させる、マルチスレッド環境で分析されました。

Javaは、視認性を確保するために揮発性の提供します。

変数がvolatile宣言されている場合、それは無効なスレッドローカルメモリを示しています。

スレッドが共有変数を変更すると、彼はすぐにメインメモリに更新されます。

別のスレッドが共有変数を読み取ると、それはメインメモリから直接読み込みます。

同期とロックが可視性を保証することができます。

整頓

注文:注文コードで実行される逐次実行プログラム。

Javaメモリー・モデルでは、効率的なコンパイラおよびプロセッサ命令の並べ替えを可能にするために、コースの並べ替えは、それがシングルスレッドの業績に影響を与えないが、影響はマルチスレッドであろう。

Javaは秩序をある程度確保するために揮発性を提供します。最も有名な例では、DCL(ダブルチェックロック)内部シングルトンパターンです。

揮発性の原則の分析

揮発性およびスレッド一定の秩序を提供していますが、原子性を保証することはできませんの可視性を保証することができます。JVMの下では、揮発性は、達成するために、「メモリバリア」です。

これらの発言の上に、2つのセマンティクスがあります。

保证可见性、不保证原子性
禁止指令重排序
复制代码

第一層は、命令の並べ替えは、以下の強調表示、意味論を導入することはできません。

命令の並べ替え

プログラムの実行時のパフォーマンスを向上させるためには、コンパイラやプロセッサは、一般的に、並べ替えの指示を行います。

编译器重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。
处理器重排序。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。
复制代码

命令並べ替えがシングルスレッドにはほとんど影響を与え、彼はプログラムの業績に影響を与えることはありませんが、マルチスレッドの有効性に影響を与えます。命令並べ替えがマルチスレッド実行の有効性に影響を与えるので、我々は、並べ替えを禁止する必要があります。JVMは、それの並べ替えを停止する方法ですか?

これが起こる-前にする原則つながります

  1. プログラムシーケンスはルール:スレッドが、コードブックの編集操作の順序に従って、たまたま-前に、後続の操作に書き込みます。
  2. 遮断ルールのロック解除操作は、事前発生ロックとロック操作の顔インチ
  3. 揮発性変数のルール:volatile変数の操作を書く、事前発生に戻ってこの変数の操作をお読みください。その裏に注意してください。
  4. ルールを渡す:Aがどうなる、前操作手順B、C Bが発生し、前の動作動作を得ることができ、操作Aが発生し、前操作Cを
  5. スレッド開始規則:各1つのアクションが起こる前に、このスレッド、メソッドのスレッドオブジェクトを起動します。
  6. スレッドがルールを破る:スレッドメソッドを中断するための呼び出しを、たまたま、前に割り込まれたスレッドコード検出に発生するイベントを中断します。
  7. ルールの最後スレッド:すべての操作中のスレッドは、すべてが事前発生を終了検出スレッド、我々はThread.join()メソッドで終了することができ、Thread.isAlive()の戻り値は、終了したスレッドを検出することを意味します。
  8. 開始オブジェクトの初期化が完了すると、発生し、前にファイナライズ()メソッド:物体側ルール

起こる-前に、後続の読み取り、volatile変数への書き込み:私たちは、揮発性のルールに焦点を当てた第三のポイントを見てください。

揮発性メモリのセマンティクスを達成するために、並べ替えは次のようである規則、JMMます。

当第二个操作是 volatile 写操作时,不管第一个操作是什么,都不能重排序。
这个规则,确保 volatile 写操作之前的操作,都不会被编译器重排序到 volatile 写操作之后。
复制代码

原則のほとんど理解して前に起こる、私たちはJVMが並べ替えを禁止されているかの質問に答えてみましょうか?

观察加入 volatile 关键字和没有加入 volatile 关键字时所生成的汇编代码发现,
加入volatile 关键字时,会多出一个 lock 前缀指令。
lock 前缀指令,其实就相当于一个内存屏障。
内存屏障是一组处理指令,用来实现对内存操作的顺序限制。
volatile 的底层就是通过内存屏障来实现的。
复制代码

上の図は、ルールに必要なメモリバリアであります:

概要

揮発性はシンプルに見えますが、それはここでは、非常に困難であることを理解することは、それらの基本的な理解です。

より軽量に関して同期やや揮発性、それはいくつかのケースで同期置き換えることができますが、完全に同期交換することはできません。唯一のいくつかのケースでは、揮発性の使用に満たさなければならない次の2つの条件にそれを使用することができるようにします:

对变量的写操作,不依赖当前值。
该变量没有包含在具有其他变量的不变式中。
复制代码

揮発性は、しばしば、次のシーンで使用される:状態フラグ変数、ダブルチェックは、複数のスレッドがスレッドを読み取り、書き込み。

おすすめ

転載: juejin.im/post/5d82e18de51d4562165535c7