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