Javaにおける揮発性vs静的


265

これはstatic、すべてのオブジェクトvolatileの値の1つのコピーを意味し、すべてのスレッドの値の1つのコピーを意味するというのは正しいでしょうか。

とにかく、static変数の値もすべてのスレッドで1つの値になるので、どうしてそうする必要があるのvolatileでしょうか。


回答:


365

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クラスを使用します。


7
volatile修飾子は、フィールドを読み取るすべてのスレッドが最新の書き込まれた値を参照できることを保証するため、変数が複数のスレッド間で共有され、この機能が必要な場合は、ユースケースによって異なります。
stivlo

5
「ローカルにキャッシュ」と言うときのキャッシュは何ですか?CPUキャッシュ、ある種のJVMキャッシュ?
マート

6
@mertinanはい、変数はプロセッサまたはコアに近いキャッシュにある可能性があります。詳細については、cs.umd.edu /〜pugh / java / memoryModel / jsr-133-faq.htmlを参照してください。
stivlo

15
「volatile」は「オブジェクトごとに1つの変数」を意味しませ。「静的」の欠如はそれを行います。OP側のこの基本的な誤解を解消できなかった場合は-1。
ローン侯爵2013

27
@EJP私は「変数を揮発性として宣言すると、オブジェクトごとに変数が1つあるので、表面上は通常の変数との違いはないようだ」という文は、静的はなく、記事を自由に編集し、文言を改善してそれをより明確にしてください。
stivlo 2013

288

静的と揮発性の違い:

静的変数:2つのスレッドが(と仮定した場合t1t2)同じオブジェクトにアクセスし、staticとして宣言された変数を更新する、それが意味t1し、t2それぞれのキャッシュに(静的変数を含む)、同じオブジェクトの独自のローカルコピーを作成することができ、更新ので、t1ローカルキャッシュ内の静的変数によって作成されたものは、キャッシュの静的変数に反映されませんt2

静的変数は、1つのオブジェクトによる更新が同じクラスの他のすべてのオブジェクトに反映されるObjectコンテキストで使用されますが、Static変数への1つのスレッドの更新がすべてのオブジェクトへの変更をすぐに反映するThreadのコンテキストでは使用されません。スレッド(ローカルキャッシュ内)。

揮発性変数:2つのスレッドが(と仮定した場合t1t2)同じオブジェクトにアクセスし、揮発性として宣言された変数を更新する、それが意味t1し、t2オブジェクトの独自のローカルキャッシュにすることができ、揮発性として宣言された変数を除いては。そのため、volatile変数にはメインコピーが1つだけあり、異なるスレッドによって更新され、1つのスレッドによってvolatile変数に対して行われた更新は、すぐに他のスレッドに反映されます。


6
@Som様こんにちは。間違いがあったら訂正してください。しかし、あなたは文を考えて「いないではなく、静的変数に1つのスレッドの更新がすべてのスレッド(そのローカルキャッシュ内)にすぐに変更が反映されますスレッドのコンテキストで。」「あるべきではなく、文脈で静的変数への1つのスレッドの更新は、すべてのスレッド(ローカルキャッシュ内)への変更をすぐには反映しません<<スレッドのスレッドの。 "
Jaikrat

@Jaikratはい、それは私にとって非常に混乱しました。書かれているように、私の理解はあなたが正しいとこの答えは間違っているということです。間違っていた場合も修正したいです。
スチュアート2015

@Jaikratスレッドは静的変数をキャッシュしませんが、更新された静的変数を参照します。
ソム2015

@Som次に、paraを修正して削除しますが、Threadのコンテキストではありません。それは非常に混乱します。ありがとう
Jaikrat

残念ながら、この答えは正しくありません。最近のCPUでは、volatile変数でさえ個別のCPUキャッシュ間で共有できます。これは、キャッシュが変更する前にキャッシュラインの排他的所有権をネゴシエートするため、問題ありません。
David Schwartz

32

他の回答に加えて、画像を1枚追加したいと思います(画像がわかりやすい)

ここに画像の説明を入力してください

static変数は個々のスレッドごとにキャッシュされます。マルチスレッド環境では、1つのスレッドがキャッシュされたデータを変更すると、他のスレッドがそのコピーを持っているため、他のスレッドに反映されない場合があります

volatile宣言により、スレッドはデータをキャッシュせず、共有コピーのみを使用します

画像ソース


1
静的変数はスレッド下のオブジェクト間で共有されますか?これは、スレッドに関係なくすべてのオブジェクト間で共有される静的変数を読み取る必要があります。
cquezel 2015

1
「揮発性変数は複数のスレッド間で共有されます(オブジェクトも同様です)。」Volatileは、変数が複数のスレッドまたはオブジェクト間で共有される方法を変更しません。ランタイムが値をキャッシュする方法を変更します。
cquezel 2015

1
静的変数に関するコメントは非静的変数にも適用され、「キャッシュされる」および「反映されない」は「キャッシュされる可能性がある」および「反映されない可能性がある」と言い換える必要があります。
cquezel

4
私はとても混乱していました。この写真は私の質問をすべてクリアしました!
vinは


4

簡単な言葉で、

  1. staticstatic変数は、オブジェクトではなくクラスに関連付けられています。クラスのすべてのインスタンスは、メモリ内の1つの固定位置にあるクラス変数を共有します

  2. volatile:このキーワードは、クラス変数とインスタンス変数の両方に適用できます。

揮発性変数を使用すると、揮発性変数への書き込みにより、その同じ変数の後続の読み取りとの前に発生する関係が確立されるため、メモリ整合性エラーのリスクが軽減されます。これは、揮発性変数への変更が常に他のスレッドから見えることを意味します

これを見てい記事によるJavin Paul より良い方法で揮発性の変数を理解するために。

ここに画像の説明を入力してください

volatileキーワードがない場合、各スレッドのスタックの変数の値は異なる場合があります。変数をvolatile、すべてのスレッドが作業メモリで同じ値を取得し、メモリの一貫性エラーが回避されます。

ここでは、用語variablestatic(クラス)変数またはinstance(オブジェクト)変数のです。

あなたのクエリについて:

とにかく、静的変数の値もすべてのスレッドで1つの値になるため、なぜ揮発性にする必要があるのでしょうか。

instanceアプリケーションで変数が必要な場合、変数を使用できませんstaticstatic変数の場合でも、図に示すようにスレッドキャッシュにより一貫性は保証されません。

使用する volatile変数すると、揮発性変数への書き込みにより、同じ変数の後続の読み取りと前に発生する関係が確立されるため、メモリ整合性エラーのリスクが軽減されます。これは、揮発性変数への変更は常に他のスレッドから見えることを意味します。

さらに、スレッドが揮発性変数を読み取るときに、揮発性への最新の変更だけでなく、変更を引き起こしたコードの副作用も表示されることも意味します。。副作用を回避するには、同期変数を使用する必要があります。しかし、Javaにはより良い解決策があります。

単純なアトミック変数アクセスを使用すると、同期コードを介してこれらの変数にアクセスするよりも効率的です

のクラスの一部 java.util.concurrentパッケージ同期に依存しないアトミックメソッドを提供ます。

詳細については、この高レベルの同時実行制御の記事を参照してください。

特に原子変数を見てください。

関連するSEの質問:

揮発性対原子

揮発性ブールvs AtomicBoolean

Javaでの揮発性と同期の違い


この回答に本当に感謝しています。私はvolatile以前のことを知っていましたが、この答えはなぜ私がまだ変数で使用volatileする必要があるのか​​私に多くを明らかにしstaticます。
Chaklader Asfak Arefe

volatile:このキーワードは、クラス変数とインスタンス変数の両方に適用できます。上記の発言は、クラスへの適用に関して正しくありません。変数に適用できるキーワードは、揮発性と一時的の2つだけです。したがって、揮発性はクラスに適用できません。
ASR

volatileは、クラス(static)変数に適用できます。Googleでダブルロックされたシングルトンリンクを確認してください。理解が間違っていることがわかります。stackoverflow.com/questions/18093735/...
ラビンドラバブー

private static volatileは有効な宣言です。
Ravindra babu

0

揮発性変数値へのアクセスは、メインメモリから直接行われます。マルチスレッド環境でのみ使用してください。静的変数が1回読み込まれます。シングルスレッド環境で使用する場合、変数のコピーが更新され、スレッドが1つしかないため、変数にアクセスしても害はありません。

静的変数がマルチスレッド環境で使用されている場合、希望する結果が期待されると問題が発生します。各スレッドには独自のコピーがあるため、あるスレッドからの静的変数のインクリメントまたはデクリメントは別のスレッドに反映されない場合があります。

静的変数から望ましい結果が期待される場合は、マルチスレッドでvolatileとstaticを使用すると、すべてが解決されます。


0

静的変数がスレッドローカルメモリにキャッシュされているかどうかは不明です。しかし、同じオブジェクト(obj)にアクセスする2つのスレッド(T1、T2)を実行し、T1スレッドによって静的変数に行われた更新は、T2に反映されました。


-2

変数を静的として宣言すると、変数のコピーは1つだけになります。したがって、異なるスレッドがその変数にアクセスするときはいつでも、変数の最終値は1つだけです(変数に割り当てられているメモリの場所は1つだけなので)。

変数が揮発性として宣言されている場合、すべてのスレッドは変数の独自のコピーを持ちますが、値はメインメモリから取得されます。したがって、すべてのスレッドの変数の値は同じになります。

したがって、どちらの場合も、変数の値はすべてのスレッドで同じであることが重要です。


15
変数が揮発性として宣言されている場合、すべてのスレッドは変数の独自のコピーを持ちますが、値はメインメモリから取得されます。=> 正しい。 したがって、すべてのスレッドの変数の値は同じになります。=> 間違っています。各スレッドは同じオブジェクトに同じ値を使用しますが、各オブジェクトには独自のコピーがあります。
stivlo 2010年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.