[解決済み】Javaにおけるvolatileとsynchronizedの違いについて
質問
という変数宣言の違いが気になります。
volatile
で、常にその変数にアクセスし
synchronized(this)
のブロックは、Java?
この記事によると http://www.javamex.com/tutorials/synchronization_volatile.shtml があり、多くの相違点がある一方で、類似点もあります。
特にこの情報には興味があります。
...
- volatile変数へのアクセスはブロックされる可能性がありません。単純な読み取りまたは書き込みを行うだけなので、同期ブロックとは異なり、ロックを保持することはありません。
- volatile変数へのアクセスはロックを保持しないので、以下のようなケースには適していません。 読み取り-更新-書き込み をアトミック操作として使用することができます(quot;miss an update"を覚悟している場合を除く)。
とはどういう意味でしょうか? 読み取り・更新・書き込み ? 書き込みは更新でもあるのか、それとも単に 更新 は読みに依存する書き込みなのでしょうか?
なによりも、どのような場合に変数を宣言するのがより適切なのかというと
volatile
を通してアクセスするのではなく
synchronized
ブロックを作成しますか?を使用するのは良いアイデアでしょうか?
volatile
を入力に依存する変数に使用できますか?例えば
render
レンダリングループで読み込まれ、キー押下イベントによって設定される?
どのように解決するのですか?
があることを理解することが重要です。 に スレッドセーフの側面。
- 実行制御、および
- メモリの可視化
1つ目は、コードがいつ実行されるか(命令の実行順序を含む)、同時に実行できるかどうかを制御することに関係し、2つ目は、実行されたことのメモリ上の効果が他のスレッドからいつ見えるようになるかに関係するものです。 各CPUはメインメモリとの間に数レベルのキャッシュを持つため、異なるCPUまたはコア上で動作するスレッドは、メインメモリのプライベートコピーを取得して作業することが許可されているため、任意の時点で異なるメモリ"を見ることができます。
使用方法
synchronized
は、他のスレッドがモニタ(またはロック)を取得するのを防ぎます。
同じオブジェクトに対して
これにより、同期化で保護されたすべてのコードブロックは
同じオブジェクトに
を同時実行しないようにする。 同期
また
あるスレッドがロックを解放するまでの間に行われたことはすべて、メモリ可視性制約を引き起こします。
が現れる。
を後から取得した別のスレッドに
同じロック
を、ロックを取得する前に発生させることができます。実際のところ、現在のハードウェアでは、モニターを取得するときに CPU キャッシュをフラッシュし、モニターを解放するときにメインメモリに書き込みますが、どちらも(比較的)高価です。
使用方法
volatile
一方、volatile変数へのすべてのアクセス(読み取りまたは書き込み)はメインメモリで行われ、volatile変数を効果的にCPUキャッシュに入れないようにします。これは、単に変数の可視性が正しく、アクセスの順序が重要でないような動作に便利です。使用方法
volatile
の扱いも変わります。
long
と
double
古い)ハードウェアではロックが必要な場合もありますが、最近の64ビットハードウェアでは必要ありません。Java 5+の新しい(JSR-133)メモリモデルでは、volatileのセマンティクスは、メモリの可視性と命令の順序に関して、synchronizedとほぼ同じ強さに強化されました(詳しくは
http://www.cs.umd.edu/users/pugh/java/memoryModel/jsr-133-faq.html#volatile
). 可視性の目的では、volatileフィールドへの各アクセスは半分の同期のように動作する。
新しいメモリ・モデルのもとでも、揮発性変数同士を並べ替えることができないのは変わりません。しかし、通常のフィールド・アクセスはそう簡単には並べ替えられなくなりました。volatileフィールドへの書き込みはモニタ・リリースと同じメモリ効果を持ち、volatileフィールドからの読み込みはモニタ・アクイジションと同じメモリ効果を持ちます。事実上、新しいメモリモデルでは、揮発性フィールドへのアクセスと他のフィールドへのアクセス(揮発性かどうかにかかわらず)の順序変更に厳しい制約を課しているため、スレッド
A
に書き込んだとき、揮発性フィールドf
がスレッドから見えるようになります。B
を読み込むとf
.
つまり、(現在のJMMでは)どちらの形式のメモリバリアも、コンパイラやランタイムがバリアを越えて命令を並べ替えることを防ぐ命令並べ替えバリアを引き起こすのですね。古いJMMでは、volatileは再順序付けを防げませんでした。これは重要なことです。なぜなら、メモリバリアを除けば、唯一の制限事項が課されるからです。 特定のスレッドに対して この場合、コードの正味の効果は、ソースに表示される順番通りに命令を実行した場合と同じになります。
volatileの用途の1つは、共有されているが不変のオブジェクトをその場で再作成し、他の多くのスレッドがその実行サイクルの特定の時点でそのオブジェクトへの参照を取得する場合です。 他のスレッドは、再作成されたオブジェクトが公開されると、それを使い始める必要がありますが、完全な同期とそれに付随する競合やキャッシュフラッシュによる追加のオーバーヘッドは必要ではありません。
// Declaration
public class SharedLocation {
static public SomeObject someObject=new SomeObject(); // default object
}
// Publishing code
// Note: do not simply use SharedLocation.someObject.xxx(), since although
// someObject will be internally consistent for xxx(), a subsequent
// call to yyy() might be inconsistent with xxx() if the object was
// replaced in between calls.
SharedLocation.someObject=new SomeObject(...); // new object is published
// Using code
private String getError() {
SomeObject myCopy=SharedLocation.someObject; // gets current copy
...
int cod=myCopy.getErrorCode();
String txt=myCopy.getErrorText();
return (cod+" - "+txt);
}
// And so on, with myCopy always in a consistent state within and across calls
// Eventually we will return to the code that gets the current SomeObject.
read-update-writeの質問についてですが、具体的には。 次のような安全でないコードを考えてみましょう。
public void updateCounter() {
if(counter==1000) { counter=0; }
else { counter++; }
}
さて、updateCounter()メソッドが非同期化されているので、2つのスレッドが同時に入る可能性があります。 何が起こるか分からないが、スレッド 1 が counter==1000 のテストを行い、それが真であると判断して中断されることがある。次にスレッド-2が同じテストを行い、それが真であることがわかり、中断される。その後、スレッド-1 が再開してカウンタを 0 にセットします。その後、スレッド-2 が再開して、スレッド-1 からの更新を逃したため、再びカウンタを 0 にセットします。 これは、私が説明したようにスレッド切り替えが発生しない場合でも、単に2つの異なるCPUコアに2つの異なるキャッシュされたカウンタのコピーが存在し、スレッドがそれぞれ別のコアで実行されたために発生する可能性があります。つまり、あるスレッドがある値でカウンターを持ち、もう一方のスレッドがキャッシュのために全く別の値でカウンターを持つこともあり得るのです。
この例で重要なのは、変数
カウンター
は、メインメモリからキャッシュに読み込まれ、キャッシュ内で更新された後、メモリバリアが発生したとき、あるいはキャッシュメモリが他のことに必要になったときに、不確定な時点でメインメモリに書き戻されるだけです。カウンターを
volatile
というのも、最大値のテストと代入は個別の操作であり、インクリメントも含めて非原子の
read+increment+write
のような機械命令です。
MOV EAX,counter
INC EAX
MOV counter,EAX
揮発性の変数は、次のような場合にのみ有効です。 すべて 例えば、完全に形成されたオブジェクトへの参照は、読み取りまたは書き込みのみです(実際、通常は1つのポイントから書き込むだけです)。他の例としては、コピーオンライトリストをバックアップする揮発性配列の参照が挙げられます。
関連
-
[解決済み] このフォーマット(Tue Jul 13 00:00:00 CEST 2010)の日付をJavaの日付に変換する方法(文字列はalfrescoのプロパティに由来しています)
-
[解決済み] Javaにおけるシンボリック参照
-
[解決済み] JavaでInputStreamを読み込んでStringに変換するにはどうすればよいですか?
-
[解決済み] JavaでNullPointerExceptionを回避する方法
-
[解決済み] JavaにおけるHashMapとHashtableの違いは何ですか?
-
[解決済み] Javaにおけるpublic、protected、package-private、privateの違いは何ですか?
-
[解決済み] プロセスとスレッドの違いは何ですか?
-
[解決済み] StringBuilderとStringBufferの違いについて
-
[解決済み] wait()とsleep()の違いについて
-
[解決済み] シンクロナイズド」とはどういう意味ですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] Java - JTextFieldが空かどうかを確認する
-
[解決済み] Cloneable throws CloneNotSupportedException
-
[解決済み] java.lang.ClassCastException: java.util.Arrays$ArrayList は java.util.ArrayList にキャストできません。
-
[解決済み] Java UnknownFormatConversionException
-
[解決済み] javac ソースファイルが見つかりません
-
[解決済み] アクティビティに割り当てられない
-
[解決済み] javaでメソッドを呼び出すプログラムのエラー修正
-
[解決済み] java.io.IOException。DER長の短い読み取り
-
[解決済み] ヘッドリカーシオンとテールリカーシオンの違い [重複]について
-
[解決済み] シンクロナイズド」とはどういう意味ですか?