「こんにちは、アミーゴ!」
「こんにちは、エリー!」
「volatile 修飾子について話したいのですが、それが何であるか知っていますか?」
「糸に関係したことだ。正確には覚えていない。」
「それでは、聞いてください。技術的な詳細をいくつか説明します。」
「コンピュータには、グローバル (通常) メモリとプロセッサに内蔵されたメモリの 2 種類のメモリがあります。プロセッサに内蔵されたメモリは、レジスタ、一次キャッシュ (L1)、二次キャッシュ (L2)、およびレジスタに分かれています。第 3 レベル (L3)。
「これらの種類のメモリには速度が異なります。最も高速で最小のメモリはレジスタで、次にプロセッサ キャッシュ (L1、L2、L3)、最後にグローバル メモリ (最も低速) です。」
「グローバル メモリとプロセッサ キャッシュの動作速度は大きく異なるため、Java マシンでは各スレッドが最も頻繁に使用される変数をローカル スレッド メモリ (プロセッサ キャッシュ内) に保存できます。」
「このプロセスをどうにか制御できないでしょうか?」
「そうではありません。すべての作業は Java マシンによって行われます。パフォーマンスの最適化に関しては、Java マシンは非常にインテリジェントです。」
「しかし、私がこれを言う理由はここにあります。小さな問題が 1 つあります。2 つのスレッドが同じ変数を処理している場合、それぞれのスレッドはコピーを独自のローカル キャッシュに保存できます。そして、1 つのスレッドは変数を変更する可能性がありますが、2 番目のスレッドは変数を変更する可能性があります。変数の独自のコピーをまだ使用しているため、変更が認識されない可能性があります。」
「それでは何ができるでしょうか?」
「Java の作成者は、この状況に備えて、volatile という特別なキーワードを提供しました。変数が別のスレッドからアクセスされる場合、Java マシンがその変数をキャッシュに入れないよう、変数を volatile 修飾子でマークする必要があります。これが通常の方法です」見た目:」
public volatile int count = 0;
「ああ、覚えています。これについてはすでに話しました。私はすでに知っています。」
「確かにそうだね。でも、私が話したときに初めて思い出したんだね。」
「あの、ちょっと忘れてたんですけど」
「繰り返しは学習の母です!」
「ここでは、volatile 修飾子に関する新しい事実をいくつか紹介します。volatile 修飾子は、変数が安全に読み書きされることを保証するだけであり、安全に変更されることを保証するものではありません。」
「違いは何ですか?」
「変数がどのように変更されるかを見てください。」
コード | 実際に何が起こるか: | 説明 |
---|---|---|
|
|
ステップ 1. 変数 count の値がグローバル メモリからプロセッサ レジスタにコピーされます。 ステップ 2. ステップ 3. |
「すごい! では、すべての変数はプロセッサ内でのみ変更されるということですか?」
「はい。」
「そして、値はメモリからプロセッサへ、またその逆にコピーされるのですか?」
「はい。」
「volatile 修飾子は、変数 count がアクセスされるときに、変数がメモリから読み取られることを保証します (ステップ 1)。また、スレッドが新しい値を割り当てたい場合、その値は確実にグローバル メモリ内にあります (ステップ 3)。」
「しかし、Java マシンは、ステップ 1 と 3 の間でスレッドの切り替えが行われないことを保証しません。」
「変数を 1 増やすには、実際には 3 回の操作が必要になるということですか?」
"はい。"
「そして、2 つのスレッドが同時に count++ を実行したい場合、それらは互いに干渉する可能性がありますか?」
「はい、チェックしてください:」
スレッド 1 | スレッド 2 | 結果 |
---|---|---|
|
|
|
「つまり、変数にアクセスすることはできますが、変更するのは依然として危険ですか?」
「まあ、変えてもいいから気をつけてね☺」
"どうやって?"
「同期は 私たちの親友です。」
"そうか。"
GO TO FULL VERSION