static
はすべてのオブジェクトの値の1つのコピーを意味し、volatile
はすべてのスレッドの値の1つのコピーを意味すると言うのは正しいですか?
とにかく、static
変数値もすべてのスレッドで1つの値になりますが、なぜvolatile
を使用する必要があるのでしょうか?
Javaでstatic変数を宣言すると、クラスのオブジェクトがいくつ作成されても、コピーは1つだけになります。 Objects
がまったく作成されていなくても、変数にアクセスできます。ただし、スレッドはローカルにキャッシュされた値を持つ場合があります。
変数がvolatileであり、staticではない場合、変数は1つになりますObject
ごとに。したがって、表面上は、通常の変数との違いはないようですが、staticとはまったく異なります。ただし、Object
フィールドがある場合でも、スレッドは変数値をローカルにキャッシュできます。
つまり、2つのスレッドが同じオブジェクトの変数を同時に更新し、その変数がvolatileとして宣言されていない場合、スレッドの1つがキャッシュに古い値を持っている可能性があります。
複数のスレッドを介してstatic値にアクセスする場合でも、各スレッドはローカルにキャッシュされたコピーを持つことができます!これを回避するには、変数をstatic volatileとして宣言します。これにより、スレッドはグローバル値を読み取るたびに強制的に読み取ります。
ただし、volatileは適切な同期の代替ではありません!
例えば:
private static volatile int counter = 0;
private void concurrentMethodWrong() {
counter = counter + 5;
//do something
counter = counter - 5;
}
concurrentMethodWrong
を同時に複数回実行すると、カウンターの最終値がゼロと異なる場合があります!
問題を解決するには、ロックを実装する必要があります。
private static final Object counterLock = new Object();
private static volatile int counter = 0;
private void concurrentMethodRight() {
synchronized (counterLock) {
counter = counter + 5;
}
//do something
synchronized (counterLock) {
counter = counter - 5;
}
}
または、 AtomicInteger
クラスを使用します。
静的と揮発性の違い:
静的変数:2つのスレッド(t1
とt2
を想定)が同じオブジェクトにアクセスし、静的として宣言されている変数を更新する場合、t1
とt2
は同じオブジェクトのローカルコピーを作成できることを意味します(そのため、ローカルキャッシュ内の静的変数に対するt1
による更新は、t2
cacheの静的変数に反映されません。
静的変数はObjectのコンテキストで使用され、1つのオブジェクトによって行われた更新は同じクラスの他のすべてのオブジェクトに反映されますただし、Threadのコンテキストではないの更新静的変数への1つのスレッドは、すべてのスレッド(ローカルキャッシュ内)への変更をすぐに反映します。
揮発性変数:2つのスレッド(t1
とt2
を想定)が同じオブジェクトにアクセスし、volatileとして宣言された変数を更新する場合、t1
とt2
がオブジェクトのローカルキャッシュを作成できることを意味します- volatileとして宣言されている変数を除くしたがって、volatile変数には、異なるスレッドによって更新されるメインコピーが1つだけあり、1つのスレッドによってvolatile変数に対して行われた更新は、すぐに他のスレッドに反映されます。
他の答えに加えて、私はそれのために1つの画像を追加したいと思います(picは理解しやすくなります)
static
変数は、個々のスレッドに対してキャッシュされる場合があります。マルチスレッド環境では、1つのスレッドがキャッシュデータを変更すると、コピーがあるため、他のスレッドに反映されない場合があります。
volatile
宣言は、スレッドがデータをキャッシュせず、共有コピーのみを使用することを確認します。
static
とvolatile
はまったく関係がないと思います。 Javaチュートリアルを読んで Atomic Access を理解し、なぜアトミックアクセスを使用するのか、 interleaved とは何かを理解することをお勧めします。答えが見つかります。
簡単な言葉で、
static :static
変数は、classではなく、 anyobjectクラスのすべてのインスタンスは、メモリ内の1つの固定位置にあるクラス変数を共有します
volatile :このキーワードは、classとinstance変数。
揮発性変数への書き込みは、同じ変数の後続の読み取りと発生前の関係を確立するため、揮発性変数を使用すると、メモリ一貫性エラーのリスクが低減します。これは、volatile変数への変更が常に他のスレッドに見えることを意味します
揮発性変数をよりよく理解するために、_-Javin Paul
による 記事 をご覧ください。
volatile
キーワードがない場合、各スレッドのスタック内の変数の値は異なる場合があります。変数をvolatile
にすることにより、すべてのスレッドが作業メモリーで同じ値を取得し、メモリーの一貫性エラーが回避されます。
ここで、variable
という用語は、static
(クラス)変数またはinstance
(オブジェクト)変数のいずれかです。
クエリについて:
とにかく、静的変数の値もすべてのスレッドで1つの値になりますが、なぜvolatileにすべきなのでしょうか?
アプリケーションでinstance
変数が必要な場合、static
変数を使用できません。 static
変数の場合でも、図に示すように、スレッドキャッシュのために一貫性は保証されません。
volatile
変数を使用すると、揮発性変数への書き込みによって、同じ変数の後続の読み取りとの発生前の関係が確立されるため、メモリ一貫性エラーのリスクが軽減されます。これは、volatile変数への変更が常に他のスレッドから見えることを意味します。
さらに、スレッドが揮発性変数を読み取ると、揮発性に対する最新の変更だけでなく、変更を引き起こしたコードの副作用も確認されることを意味します=>揮発性変数ではメモリ整合性エラーが引き続き発生する可能性があります。副作用を回避するには、同期変数を使用する必要があります。しかし、Javaにはより優れたソリューションがあります。
単純なアトミック変数アクセスの使用は、同期コードを介してこれらの変数にアクセスするよりも効率的です
Java.util.concurrent
パッケージの一部のクラスは、同期に依存しないアトミックメソッドを提供します。
詳細については、こちらの 高レベルの同時実行制御 の記事を参照してください。
特に Atomic variables をご覧ください。
関連するSEの質問:
揮発性変数値へのアクセスは、メインメモリから直接行われます。マルチスレッド環境でのみ使用する必要があります。静的変数は1回ロードされます。シングルスレッド環境で使用すると、変数のコピーが更新され、スレッドが1つしかないため、変数にアクセスしても害はありません。
静的変数がマルチスレッド環境で使用される場合、望ましい結果が期待される場合は問題が発生します。各スレッドには独自のコピーがあるため、あるスレッドからの静的変数の増分または減分は、別のスレッドに反映されない場合があります。
静的変数から目的の結果が期待される場合、マルチスレッドで揮発性と静的を使用すると、すべてが解決されます。
静的変数がスレッドローカルメモリにキャッシュされているかどうかは不明です。しかし、同じオブジェクト(obj)にアクセスする2つのスレッド(T1、T2)を実行し、T1スレッドが静的変数に更新すると、T2に反映されます。